logo 范 · 拾光录
网址收集 关于作者 Github Gitee
杂文随笔4
Hexo博客:基础使用Hexo博客:Next主题Hexo博客:Next进阶使用Hexo博客:Next高级配置
前端编程3
前端基础知识16
UniApp15
Vue框架19
Java编程7
Java基础知识14
SpringBoot31
SpringMVC14
MyBatis8
SpringCloud15
RocketMQ1
MyCat1
数据库4
MySQL6
Redis7
MongoDB10
H21
Java技术栈4
知识整理分布式锁的实现方案MySQL事务MySQL的锁
软件工具箱14
IDEAGitMavenGradleNginx安装Nginx配置JMeter压测OllamaPicGoRustFSVSCodeDockerObsidianObs录制
Linux知识11
树莓派安装及使用ArchLinux:常用软件ArchLinux:基础系统安装ArchLinux:深度优化ArchLInux:图形化界面安装ArchLinux:Nirifrp内网穿透Jar启动脚本Linux常用命令VirtualBox安装CentOSVirtualBox安装Ubuntu
Python编程6
Python基础知识Python语法yolo目标检测OpenCV的使用及树莓派平台condauv
创意设计2
Blender:入门知识UI设计基础知识
AI相关9
Claude CodeHermes AgentOpenAI基本使用OpenAI工具调用OpenAI记忆管理OpenAI推理执行OpenAI开发框架Langchainllama.cpp

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 "未知";
    }
}
Nginx配置
文档
使用示例