package org.example.util;
|
|
import com.google.gson.Gson;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.lionsoul.ip2region.xdb.Searcher;
|
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.server.ServerHttpRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.io.BufferedReader;
|
import java.io.InputStream;
|
import java.io.InputStreamReader;
|
import java.net.*;
|
import java.nio.charset.StandardCharsets;
|
import java.util.Enumeration;
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.concurrent.TimeUnit;
|
|
/**
|
* @program: demo
|
* @description:
|
* @create: 2024-07-29 11:50
|
**/
|
|
@Slf4j
|
public class IpAddressUtil {
|
|
// ip2region.xdb 文件地址常量(本地xdb文件路径)
|
public static String XDB_PATH = PropertiesUtil.getProperty("XDB_PATH");
|
|
/**
|
* 获取IP地址:
|
*/
|
public static String getIpAddress(HttpServletRequest request) {
|
// 获取客户端真实 IP 地址
|
String ipAddress = request.getHeader("X-Forwarded-For");
|
|
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
|
ipAddress = request.getHeader("Proxy-Client-IP");
|
}
|
|
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
|
ipAddress = request.getHeader("WL-Proxy-Client-IP");
|
}
|
|
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
|
ipAddress = request.getRemoteAddr();
|
}
|
|
return ipAddress;
|
}
|
|
/**
|
* 获取mac地址
|
*/
|
public static String getMacIpAddress() {
|
try {
|
InetAddress inetAddress = InetAddress.getLocalHost();
|
byte[] macAddressBytes = NetworkInterface.getByInetAddress(inetAddress).getHardwareAddress();
|
// 将mac地址拼装成String
|
StringBuilder sb = new StringBuilder();
|
for (int i = 0; i < macAddressBytes.length; i++) {
|
if (i != 0) {
|
sb.append("-");
|
}
|
// mac[i] & 0xFF 是为了把byte转化为正整数
|
String s = Integer.toHexString(macAddressBytes[i] & 0xFF);
|
sb.append(s.length() == 1 ? 0 + s : s);
|
}
|
return sb.toString().trim().toUpperCase();
|
} catch (Exception e) {
|
log.error("Mac获取IP地址异常,{}", e.getMessage());
|
}
|
return "";
|
}
|
|
/**
|
* 方法一:完全基于ip2region.xdb文件,对用户ip地址进行转换
|
* 注:并发调用时,每个线程需创建一个独立的searcher对象 单独使用。
|
*/
|
public static String getIpPossessionByFile(String ip) {
|
if (StringUtils.isNotEmpty(ip)) {
|
try {
|
// 1、创建 searcher 对象
|
Searcher searcher = Searcher.newWithFileOnly(XDB_PATH);
|
// 2、查询
|
long sTime = System.nanoTime();
|
String region = searcher.search(ip);
|
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
|
region = region.replace("|0", "");
|
//log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
|
return region;
|
} catch (Exception e) {
|
e.printStackTrace();
|
log.error("获取IP地址异常:{} ", e.getMessage());
|
throw new RuntimeException("获取IP地址异常");
|
}
|
}
|
return "未知";
|
}
|
|
/**
|
* 方法二:缓存 VectorIndex 索引,对用户ip地址进行转换
|
* 注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局变量 vIndex 缓存。
|
*/
|
public static String getCityInfoByVectorIndex(String ip) {
|
if (StringUtils.isNotEmpty(ip)) {
|
try {
|
// 1、从 XDB_PATH 中预先加载 VectorIndex 缓存,并且作为全局变量,后续反复使用。
|
byte[] vIndex = Searcher.loadVectorIndexFromFile(XDB_PATH);
|
// 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
|
Searcher searcher = Searcher.newWithVectorIndex(XDB_PATH, vIndex);
|
// 3、查询
|
long sTime = System.nanoTime();
|
String region = searcher.search(ip);
|
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
|
region = region.replace("|0", "");
|
//log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
|
return region;
|
} catch (Exception e) {
|
log.error("获取IP地址异常:{} ", e.getMessage());
|
throw new RuntimeException("获取IP地址异常");
|
}
|
}
|
return "未知";
|
}
|
|
/**
|
* 方法三:缓存整个 xdb 数据,对用户ip地址进行转换
|
* 注:并发使用时,用整个 xdb 数据缓存创建的查询对象可以安全的用于并发,也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。
|
*/
|
public static String getCityInfoByMemorySearch(String ip) {
|
if (StringUtils.isNotEmpty(ip)) {
|
try {
|
// 1、从 XDB_PATH 加载整个 xdb 到内存。
|
byte[] cBuff = Searcher.loadContentFromFile(XDB_PATH);
|
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
|
Searcher searcher = Searcher.newWithBuffer(cBuff);
|
// 3、查询
|
long sTime = System.nanoTime();
|
String region = searcher.search(ip);
|
long cost = TimeUnit.NANOSECONDS.toMicros(System.nanoTime() - sTime);
|
region = region.replace("|0", "");
|
//log.info("{地区: {}, IO操作数: {}, 耗时: {} μs}", region, searcher.getIOCount(), cost);
|
return region;
|
} catch (Exception e) {
|
log.error("获取IP地址异常:{} ", e.getMessage());
|
throw new RuntimeException("获取IP地址异常");
|
}
|
}
|
return "未知";
|
}
|
|
/**
|
* 方法四:在线获取IP地址
|
* 注:通过别人或者官网提供的API接口去实现查询的功能,弊端就是特别依赖别人的服务器,一旦服务器宕机就无法访问了。
|
*/
|
public static String getIpAddressByOnline(String ip) {
|
|
try {
|
//1、创建 URLConnction
|
URL url = new URL("http://ip-api.com/json/" + ip + "?lang=zh-CN");
|
|
//2、设置connection的属性
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
connection.setRequestMethod("GET");
|
connection.setConnectTimeout(20000);
|
connection.setReadTimeout(20000);
|
connection.setRequestProperty("content-type", "application/json; charset=utf-8");
|
|
//3.连接
|
connection.connect();
|
|
//4.获取内容
|
InputStream inputStream = connection.getInputStream();
|
|
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
|
String line;
|
StringBuilder sb = new StringBuilder();
|
while ((line = br.readLine()) != null) {
|
sb.append(line);
|
}
|
br.close();
|
//System.out.println(sb);
|
|
String str = sb.toString();
|
if (StringUtils.isNotEmpty(str)) {
|
// string转map
|
Gson gson = new Gson();
|
Map<String, Object> map = new HashMap<>();
|
map = gson.fromJson(str, map.getClass());
|
String country = (String) map.get("country");
|
String city = (String) map.get("city");
|
String regionName = (String) map.get("regionName");
|
//log.info("【国家】{},【城市】{},【地区】{}", country, city, regionName);
|
return country + "|" + city + "|" + regionName;
|
}
|
} catch (Exception e) {
|
log.error("在线查询IP地址异常,{}", e.getMessage());
|
throw new RuntimeException("在线查询IP地址异常");
|
}
|
return null;
|
}
|
|
/**
|
* 根据IP地址 获取归属地
|
*/
|
public static String getIpPossession(String ipAddress) {
|
if (StringUtils.isNotEmpty(ipAddress)) {
|
ipAddress = ipAddress.replace("|", " ");
|
String[] cityList = ipAddress.split(" ");
|
if (cityList.length > 0) {
|
// 国内的显示到具体的省
|
if ("中国".equals(cityList[0])) {
|
if (cityList.length > 1) {
|
return cityList[1];
|
}
|
}
|
// 国外显示到国家
|
return cityList[0];
|
}
|
}
|
return "未知";
|
}
|
|
public static void main(String[] args) {
|
|
String ip = "26.26.26.1";// 国内IP
|
String abroadIp = "26.26.26.1"; // 国外IP
|
|
System.out.println("方法一(国内):" + getIpPossessionByFile(ip));
|
System.out.println("方法二(国内):" + getCityInfoByVectorIndex(ip));
|
System.out.println("方法三(国内):" + getCityInfoByMemorySearch(ip));
|
System.out.println("方法四(国内):" + getIpAddressByOnline(ip));
|
|
System.out.println("方法一(国外):" + getIpPossessionByFile(abroadIp));
|
System.out.println("方法二(国外):" + getCityInfoByVectorIndex(abroadIp));
|
System.out.println("方法三(国外):" + getCityInfoByMemorySearch(abroadIp));
|
System.out.println("方法四(国外):" + getIpAddressByOnline(abroadIp));
|
|
//System.out.println("归属地(国内):" + getIpPossession(getCityInfoByVectorIndex(ip)));
|
//System.out.println("归属地(国外):" + getIpPossession(getCityInfoByVectorIndex(abroadIp)));
|
|
}
|
|
}
|