Nginx配置
如果走Nginx,需要配置,具体看本站的Nginx配置文档
文档
IP2REGION官网和Github地址,用到的xdb文件可在Github仓库下载
使用示例
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 访问拦截器
*/
@Slf4j
public class VisitInterceptor implements HandlerInterceptor {
// 全局唯一的 Ip2Region 查询实例
private static volatile Ip2Region ip2Region;
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) {
// 延迟加载:第一次有请求时,才去初始化 ip2Region
if (ip2Region == null) {
synchronized (VisitInterceptor.class) {
if (ip2Region == null) {
initIp2Region();
}
}
}
System.out.println("拦截器:" + request.getRequestURI());
String realIp = getRealIp(request);
System.out.println("访问IP:" + realIp);
// 调用归属地查询
String region = ipRegion(realIp);
System.out.println("IP归属地:" + region);
return true;
}
/**
* 初始化 IP 归属地查询服务(通过 InputStream 读取 resources 下的文件)
*/
private void initIp2Region() {
try {
// 通过当前类的类加载器获取 resources 下文件的输入流
// 假设你的文件放在 src/main/resources/ip2region/ 目录下
var v4Is = VisitInterceptor.class.getClassLoader().getResourceAsStream("ip2region/ip2region_v4.xdb");
var v6Is = VisitInterceptor.class.getClassLoader().getResourceAsStream("ip2region/ip2region_v6.xdb");
if (v4Is == null || v6Is == null) {
throw new RuntimeException("在 resources 目录下找不到 ip2region 的 xdb 文件,请检查路径!");
}
// 1. 创建 v4 的配置
final Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache) // 全内存缓存,性能最佳
.setXdbInputStream(v4Is) // 改为使用 InputStream 加载
.asV4();
// 2. 创建 v6 的配置
final Config v6Config = Config.custom()
.setCachePolicy(Config.BufferCache)
.setXdbInputStream(v6Is) // 改为使用 InputStream 加载
.asV6();
// 3. 创建 Ip2Region 查询服务
ip2Region = Ip2Region.create(v4Config, v6Config);
System.out.println("IP归属地查询服务初始化成功!");
} catch (Exception e) {
System.err.println("IP归属地查询服务初始化失败:" + e.getMessage());
log.error("IP归属地查询服务初始化失败:", e);
}
}
private static String getRealIp(HttpServletRequest request) {
// 1. 优先从 X-Forwarded-For 获取(应对多层代理)
String ip = request.getHeader("X-Forwarded-For");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实客户端IP
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index).trim();
} else {
return ip;
}
}
// 2. 其次从 X-Real-IP 获取
ip = request.getHeader("X-Real-IP");
if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
// 3. 兜底方案:直接获取远程地址
return request.getRemoteAddr();
}
/**
* 根据 IP 获取归属地
*/
private static String ipRegion(String ip) {
// 1. 防御性编程:如果初始化失败或服务未启动,直接返回未知
if (ip2Region == null) {
return "未知";
}
// 2. 过滤本地回环地址和内网地址
if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip) || ip.startsWith("192.168")) {
return "内网IP";
}
try {
// 3. 执行查询
String region = ip2Region.search(ip);
// 4. 提取核心归属地信息
if (region != null && !region.isEmpty()) {
String[] parts = region.split("\\|");
// parts[2] 是省份,parts[3] 是城市
String province = (parts.length > 2 && !"0".equals(parts[2])) ? parts[2] : "";
String city = (parts.length > 3 && !"0".equals(parts[3])) ? parts[3] : "";
// 拼接返回,例如 "贵州省贵阳市"
return province + city;
}
} catch (Exception e) {
System.err.println("查询IP归属地异常,IP: " + ip + ",原因: " + e.getMessage());
}
return "未知";
}
}