当前位置: 代码网 > it编程>编程语言>Java > SpringBoot3集成ip2region实现离线IP查询方案

SpringBoot3集成ip2region实现离线IP查询方案

2026年03月30日 Java 我要评论
引言做后端开发,ip归属地查询绝对是高频刚需场景:用户访问日志埋点、地域权限风控、用户地域画像、站点访问统计,几乎处处都能用得上。之前项目里用过不少第三方在线ip查询接口,踩坑无数—&md

引言

做后端开发,ip归属地查询绝对是高频刚需场景:用户访问日志埋点、地域权限风控、用户地域画像、站点访问统计,几乎处处都能用得上。之前项目里用过不少第三方在线ip查询接口,踩坑无数——高峰期接口限流、服务不稳定宕机,还存在用户ip隐私合规风险,后来改用ip2region这款纯离线开源查询方案,直接解决了所有痛点。

对比下来,基于ip2region搭建的离线ip查询方案,才更适配生产场景,全程本地运行、零外网依赖,单ip查询速度达微秒级,常规场景精准度完全够用,而且免费开源、无调用限制。这篇文章全程只讲实操、不堆砌理论,代码复制即可运行,从环境配置到生产避坑一站式讲透,直接照搬就能用到正式项目里。

一、项目环境与核心资源

先同步实操环境版本,避开版本兼容坑,本文全程基于ip2region 3.3.6最新稳定版开发,适配springboot3生态:

  • jdk 17+
  • springboot 3.2.5
  • ip2region 3.3.6

核心资源说明:ip2region 官方拆分了ipv4和ipv6两套独立离线数据库,需分别下载对应xdb文件,这是实现双协议查询的关键,旧版单文件无法同时支持两种ip,必须下载官方最新版双库文件,避免数据不匹配、查询失败问题。

ip2region双库文件下载地址(gitee官方): 1. ipv4专用库:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb 2. ipv6专用库:https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb 下载后不要重命名、不要修改后缀,严格保留原名区分双库,贴合ip2region官方加载规范。

二、maven依赖引入

打开项目 pom.xml,直接引入ip2region核心依赖,springboot3 无需额外适配包,极简引入即可,额外推荐引入hutool工具包,简化后续ip获取和字符串处理,生产项目必备:

<!-- ip2region 离线ip归属地查询核心依赖 最新3.3.6版本 -->
<dependency>
    <groupid>org.lionsoul</groupid>
    <artifactid>ip2region</artifactid>
    <version>3.3.6</version>
</dependency>
<!-- 可选:hutool工具包,简化ip获取和字符串处理,生产常用 -->
<dependency>
    <groupid>cn.hutool</groupid>
    <artifactid>hutool-all</artifactid>
    <version>5.8.25</version>
</dependency>

依赖刷新完成后,直接进入下一步数据库文件配置,全程贴合ip2region官方api写法,无需额外编写复杂配置类,上手即用。

三、xdb数据库文件放置

这一步很关键,ip2region双库文件必须规范放置,避免加载冲突,亲测最优路径方案:

  1. 在项目 resources 目录下,直接新建文件夹 ipdb(无需额外建子目录)
  2. 将下载好的 ip2region_v4.xdb(ipv4库)、ip2region_v6.xdb(ipv6库),直接放入 resources/ipdb 目录下
  3. 严格保留文件原名,不重命名、不修改后缀,防止ip2region加载路径匹配失败

文件放置完成后,目录结构参考下图:

重点避坑:打包时如果出现 xdb 文件丢失、损坏,需要在 pom.xml 的 build 配置中,禁止压缩 xdb 后缀文件,后面避坑章节会详细讲,双库文件都需要生效该配置。

四、封装通用ip查询工具类

ip2region 官方针对ipv4、ipv6双库做了统一api封装,不用手动维护两个独立searcher,直接通过config分别配置双库、再由ip2region统一托管即可,使用更简洁、稳定性更强。我采用单例模式,项目启动时一次性加载双库文件,避免重复io占用内存,服务关闭时统一释放资源,完全适配springboot3资源加载规范,全局可直接调用,代码复制即可落地:

import jakarta.annotation.postconstruct;
import jakarta.annotation.predestroy;
import lombok.extern.slf4j.slf4j;
import org.lionsoul.ip2region.service.config;
import org.lionsoul.ip2region.service.ip2region;
import org.springframework.core.io.classpathresource;
import org.springframework.stereotype.component;
import java.io.inputstream;
/**
 * 离线ip归属地查询工具类
 * 遵循官方双库规范:ipv4+ipv6分开配置,统一托管查询
 * 完整覆盖所有内网ip段判断,杜绝公网查询误判
 * 单例模式,全局复用,避免重复加载数据库
 */
@slf4j
@component
public class ipregionutil {
    /**
     * ipv4、ipv6双库文件路径,对应resources/ipdb目录
     * 严格对应ip2region官方原文件名,不修改、不重命名
     */
    private static final string ipv4_xdb_path = "ipdb/ip2region_v4.xdb";
    private static final string ipv6_xdb_path = "ipdb/ip2region_v6.xdb";
    /**
     * ip2region官方统一查询对象,托管双库,无需手动拆分searcher
     */
    private ip2region ip2region;
    /**
     * 项目启动初始化,加载ip2region双库文件
     * 采用buffercache缓存策略,兼顾查询速度与内存占用
     */
    @postconstruct
    public void init() {
        try {
            // 加载ipv4数据库配置
            classpathresource v4resource = new classpathresource(ipv4_xdb_path);
            inputstream v4is = v4resource.getinputstream();
            config v4config = config.custom()
                    .setcachepolicy(config.buffercache)
                    .setsearchers(10)
                    .setxdbinputstream(v4is)
                    .asv4();
            // 加载ipv6数据库配置
            classpathresource v6resource = new classpathresource(ipv6_xdb_path);
            inputstream v6is = v6resource.getinputstream();
            config v6config = config.custom()
                    .setcachepolicy(config.buffercache)
                    .setsearchers(10)
                    .setxdbinputstream(v6is)
                    .asv6();
            // 统一创建ip2region查询对象
            ip2region = ip2region.create(v4config, v6config);
            log.info("ip2region初始化成功,ipv4+ipv6双库加载完成");
        } catch (exception e) {
            log.error("ip2region初始化失败,请检查双库文件路径和完整性", e);
            throw new runtimeexception("ip2region异常,无法提供ip查询服务");
        }
    }
    /**
     * 统一ip查询入口,自动识别ipv4/ipv6
     * 完整过滤所有内网ip,包含10段、172段、192段、本地回环
     */
    public string getipregion(string ip) {
        try {
            // 空ip直接返回
            if (ip == null || ip.isblank()) {
                return "内网ip|内网ip";
            }
            // 判断是否为内网ip,包含ipv4全内网段 + ipv6回环
            if (isprivateip(ip)) {
                return "内网ip|内网ip";
            }
            // 调用ip2region执行公网ip查询
            return ip2region.search(ip);
        } catch (exception e) {
            log.error("ip2region ip查询异常,ip:{}", ip, e);
            return "查询失败";
        }
    }
    /**
     * 完整内网ip判断逻辑
     * ipv4内网范围:
     * 1. 127.0.0.1 本地回环
     * 2. 10.0.0.0 ~ 10.255.255.255
     * 3. 172.16.0.0 ~ 172.31.255.255
     * 4. 192.168.0.0 ~ 192.168.255.255
     * ipv6内网:::1 / 0:0:0:0:0:0:0:1 回环地址
     */
    private boolean isprivateip(string ip) {
        // ipv4内网判断
        if (ip.contains(".")) {
            // 本地回环
            if ("127.0.0.1".equals(ip)) {
                return true;
            }
            // 192.168网段
            if (ip.startswith("192.168.")) {
                return true;
            }
            // 10网段
            if (ip.startswith("10.")) {
                return true;
            }
            // 172.16.0.0 ~ 172.31.255.255 内网段
            if (ip.startswith("172.")) {
                string[] parts = ip.split("\\.");
                if (parts.length == 4) {
                    try {
                        int secondsegment = integer.parseint(parts[1]);
                        return secondsegment >= 16 && secondsegment <= 31;
                    } catch (numberformatexception e) {
                        return false;
                    }
                }
            }
            return false;
        }
        // ipv6回环地址判断
        return "::1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip);
    }
    /**
     * 服务关闭,释放ip2region资源
     */
    @predestroy
    public void close() {
        try {
            if (ip2region != null) {
                ip2region.close();
                log.info("ip2region资源释放完成");
            }
        } catch (exception e) {
            log.error("ip2region资源释放失败", e);
        }
    }
}

工具类核心说明(贴合ip2region 3.3.6官方规范):

  • 严格遵循ip2region官方api规范,采用config+ip2region统一托管双库,摒弃旧版searcher手动维护模式,代码更简洁、无调用冲突,兼容性拉满
  • 全内网ip覆盖判断:完整包含 10 段、172.16-31 段、192.168 段、ipv4 回环及 ipv6 回环地址,彻底杜绝内网ip误查公网
  • 自动识别ipv4/ipv6地址,一套接口适配双协议,业务调用无需区分ip类型,无感兼容双网络环境
  • 选用buffercache缓存策略,平衡查询速度与内存占用,低配服务器可灵活切换为nocache,适配不同部署环境
  • spring bean单例生命周期管理,项目启动一次性加载双库,服务关闭自动释放资源,杜绝内存泄漏,长期运行稳定
  • 全局异常捕获完善,启动失败直接抛出明确异常,方便运维快速排查,避免后续空指针问题

五、编写测试接口,快速验证双协议功能

写一个极简的controller测试接口,支持两种查询模式:手动传入指定ip查询(兼容ipv4+ipv6)、自动获取当前请求ip查询,适配本地调试、nginx代理、ipv6部署场景,基于ip2region快速验证功能是否正常:

import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.web.bind.annotation.getmapping;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestparam;
import org.springframework.web.bind.annotation.restcontroller;
import javax.servlet.http.httpservletrequest;
@slf4j
@restcontroller
@requestmapping("/ip")
@requiredargsconstructor
public class ipcontroller {
    private final ipregionutil ipregionutil;
    /**
     * 手动传入ip查询归属地
     * @param ip 目标ip
     * @return 归属地结果
     */
    @getmapping("/query")
    public string queryip(@requestparam string ip) {
        return ipregionutil.getipregion(ip);
    }
    /**
     * 获取当前请求的真实ip并查询归属地
     * 适配nginx、反向代理场景,解决内网ip获取问题
     */
    @getmapping("/current")
    public string getcurrentipregion(httpservletrequest request) {
        // 优先获取代理转发的真实客户端ip
        string realip = request.getheader("x-real-ip");
        if (realip == null || realip.isblank()) {
            realip = request.getremoteaddr();
        }
        log.info("当前请求真实客户端ip:{}", realip);
        return ipregionutil.getipregion(realip);
    }
}

六、启动项目,接口测试

1. 启动springboot项目,观察控制台日志,出现 ip2region初始化成功,ipv4+ipv6双库加载完成 字样,说明双库文件加载正常,核心工具初始化完成

2. 打开postman或者浏览器,调用测试接口:

  • 公网ipv4测试:http://localhost:8080/api/ip/query?ip=114.114.114.114
  • 内网ipv4测试(172段):http://localhost:8080/api/ip/query?ip=172.16.2.3
  • 内网ipv4测试(10段):http://localhost:8080/api/ip/query?ip=10.0.0.5
  • 公网ipv6测试:http://localhost:8080/api/ip/query?ip=2408:8888:8888::8888
  • 当前请求ip查询:http://localhost:8080/api/ip/current

ipv4正常返回结果示例:中国|江苏省|南京市|0|cn

ipv6正常返回结果示例:中国|北京|北京市|0|cn

各类内网ip(10段、172段、192段、本地回环)测试结果:内网ip|内网ip,过滤逻辑完全生效

七、常见坑与解决方案

1. 打包后找不到xdb文件,项目启动失败

原因:maven打包时,默认压缩资源文件,导致ip2region依赖的xdb文件损坏;或者双库文件路径写错、少放其中一个库文件。

解决方案:在 pom.xml 的 build 节点中,添加以下配置,禁止压缩 xdb 后缀文件:

<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <excludes>
                <exclude>*.xdb</exclude>
            </excludes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>*.xdb</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

2. 反向代理后,获取不到真实客户端ip

生产项目基本都用nginx代理,直接用request.getremoteaddr()获取的是服务器内网ip,需要nginx配置转发真实ip,确保ip2region能查询到真实用户ip:

proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;

java代码中优先读取 x-real-ip 请求头,和我上面controller代码一致。

八、生产进阶用法

  • 结果格式化:ip2region默认返回带|分隔符,可封装成vo对象,拆分国家、省份、城市、isp字段,返回前端更友好,ipv4/ipv6查询结果格式完全一致
  • 本地缓存:高频查询ip,加caffeine或redis缓存,减少重复调用ip2region,提升性能,ipv4和ipv6地址可共用一套缓存逻辑
  • ipv6部署适配:服务器开启ipv6后,无需修改代码,接口可直接接收ipv6地址,工具类自动识别并调用ipv6专用库查询,开箱即用
  • 定时更新双库:ip2region官方会单独更新ipv4、ipv6数据库,定时下载对应最新xdb文件,替换对应目录文件,重启服务即可,保证双协议数据精准度

九、总结

springboot3集成ip2region实现离线ip查询,全程不到15分钟就能落地,纯本地运行、零外网依赖、严格遵循官方双库规范支持ipv4+ipv6、查询性能拉满,完全能满足中小项目到生产级别的ip查询需求,不管是传统ipv4网络,还是新部署的ipv6环境,都能稳定适配。整个过程没有复杂配置,工具类和接口代码直接复用即可,文中的避坑方案都是实战踩坑总结,照着操作基本不会出现异常。

对比第三方在线接口,ip2region离线方案更稳定、更安全,还没有调用次数限制,也不用担心隐私合规风险,强烈建议大家把项目里的在线ip接口替换成这套方案,上手成本极低,实用性拉满!

以上就是springboot3集成ip2region实现离线ip查询方案的详细内容,更多关于springboot3 ip2region离线ip查询的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com