当前位置:

首页 > 编程开发 > Spring Boot怎么快速实现IP地址解析

Spring Boot怎么快速实现IP地址解析

引入:如果使用本地ip解析的话,我们将会借助ip2region,该项目维护了一份较为详细的本地ip地址对应表,如果为了离线环境的使用,需要导入该项目依赖,并指定版本,不同版本的方法可能存在差异。org.lionsoulip2region2.6.3开发:在使用时需要将xdb文件下载到工程文件目录下,使用ip2region即使是完全基于xdb文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:vIndex索引缓存:使用固定的512KiB的内存空间缓存vectorindex数据,减少一

本文目录

    引入:

    如果使用本地ip 解析的话,我们将会借助ip2region,该项目维护了一份较为详细的本地ip 地址对应表,如果为了离线环境的使用,需要导入该项目依赖,并指定版本,不同版本的方法可能存在差异。

    
    
    	org.lionsoul
    	ip2region
    	2.6.3
            

    开发:

    在使用时需要将 xdb 文件下载到工程文件目录下,使用ip2region即使是完全基于 xdb 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:

    • vIndex 索引缓存 :使用固定的 512KiB 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作,保持平均查询效率稳定在10-20微秒之间。

    • xdb 整个文件缓存:将整个 xdb 文件全部加载到内存,内存占用等同于 xdb 文件大小,无磁盘 IO 操作,保持微秒级别的查询效率。

    /**
     * ip查询
     */
    @Slf4j
    public class IPUtil {
        private static final String UNKNOWN = "unknown";
        protected IPUtil(){ }
        /**
         * 获取 IP地址
         * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
         * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
         * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ip = request.getHeader("x-forwarded-for");
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
            return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
        }
        public static  String getAddr(String ip){
            String dbPath = "src/main/resources/ip2region/ip2region.xdb";
            // 1、从 dbPath 加载整个 xdb 到内存。
            byte[] cBuff;
            try {
                cBuff = Searcher.loadContentFromFile(dbPath);
            } catch (Exception e) {
                log.info("failed to load content from `%s`: %s\n", dbPath, e);
                return null;
            }
            // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
            Searcher searcher;
            try {
                searcher = Searcher.newWithBuffer(cBuff);
            } catch (Exception e) {
               log.info("failed to create content cached searcher: %s\n", e);
                return null;
            }
            // 3、查询
            try {
                String region = searcher.searchByStr(ip);
                return region;
            } catch (Exception e) {
                log.info("failed to search(%s): %s\n", ip, e);
            }
            return null;
        }

    这里我们将ip 解析封装成一个工具类,包含获取IP和ip 地址解析两个方法,ip 的解析可以在请求中获取。获取到ip后,需要根据ip ,在xdb 中查找对应的IP地址的解析,由于是本地数据库可能存在一定的缺失,部分ip 存在无法解析的情况。

    在线解析:

    如果想要获取更加全面的ip 地址信息,可使用在线数据库,这里提供的是 whois.pconline.com 的IP解析,该IP解析在我的使用过程中表现非常流畅,而且只有少数的ip 存在无法解析的情况。

    @Slf4j
    public class AddressUtils {
        // IP地址查询
        public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    
        // 未知地址
        public static final String UNKNOWN = "XX XX";
    
        public static String getRealAddressByIP(String ip) {
            String address = UNKNOWN;
            // 内网不查询
            if (IpUtils.internalIp(ip)) {
                return "内网IP";
            }
            if (true) {
                try {
                    String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true" ,"GBK");
                    if (StrUtil.isEmpty(rspStr)) {
                        log.error("获取地理位置异常 {}" , ip);
                        return UNKNOWN;
                    }
                    JSONObject obj = JSONObject.parseObject(rspStr);
                    String region = obj.getString("pro");
                    String city = obj.getString("city");
                    return String.format("%s %s" , region, city);
                } catch (Exception e) {
                    log.error("获取地理位置异常 {}" , ip);
                }
            }
            return address;
        }
        public static String sendGet(String url, String param, String contentType) {
            StringBuilder result = new StringBuilder();
            BufferedReader in = null;
            try {
                String urlNameString = url + "?" + param;
                log.info("sendGet - {}" , urlNameString);
                URL realUrl = new URL(urlNameString);
                URLConnection connection = realUrl.openConnection();
                connection.setRequestProperty("accept" , "*/*");
                connection.setRequestProperty("connection" , "Keep-Alive");
                connection.setRequestProperty("user-agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
                connection.connect();
                in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
                String line;
                while ((line = in.readLine()) != null) {
                    result.append(line);
                }
                log.info("recv - {}" , result);
            } catch (ConnectException e) {
                log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
            } catch (SocketTimeoutException e) {
                log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
            } catch (IOException e) {
                log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
            } catch (Exception e) {
                log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
            } finally {
                try {
                    if (in != null) {
                        in.close();
                    }
                } catch (Exception ex) {
                    log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
                }
            }
            return result.toString();
        }
    }

    场景:

    那么在开发的什么流程获取ip 地址是比较合适的,这里就要用到我们的拦截器了。拦截进入服务的每个请求,进行前置操作,在进入时就完成请求头的解析,ip 获取以及ip 地址解析,这样在后续流程的全环节,都可以复用ip 地址等信息。

    /**
     * 对ip 进行限制,防止IP大量请求
     */
    @Slf4j
    @Configuration
    public class IpUrlLimitInterceptor implements HandlerInterceptor{
    
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
    
            //更新全局变量
            Constant.IP = IPUtil.getIpAddr(httpServletRequest);
            Constant.IP_ADDR = AddressUtils.getRealAddressByIP(Constant.IP);
            Constant.URL = httpServletRequest.getRequestURI();
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
            //通过本地获取
    //        获得ip
    //        String ip = IPUtil.getIpAddr(httpServletRequest);
            //解析具体地址
    //        String addr = IPUtil.getAddr(ip);
    
            //通过在线库获取
    //        String ip = IpUtils.getIpAddr(httpServletRequest);
    //        String ipaddr = AddressUtils.getRealAddressByIP(ipAddr);
    //        log.info("IP >> {},Address >> {}",ip,ipaddr);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    
        }
    }
    如果想要执行我们的ip 解析拦截器,需要在spring boot的视图层进行拦截才会触发我们的拦截器。
    @Configuration
    public class WebConfig implements WebMvcConfigurer {
        @Autowired
        IpUrlLimitInterceptor ipUrlLimitInterceptor;
    	
    	    //执行ip拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry){
            registry.addInterceptor(ipUrlLimitInterceptor)
                    // 拦截所有请求
                    .addPathPatterns("/**");
        }
    }
    通过这样的一套流程下来,我们就能实现对每一个请求进行ip 获取、ip解析,为每个请求带上具体ip地址的小尾巴。
    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 ip地址
    相关文章 更多
    电脑网线连接不上网络怎么回事排查与解决教程
    电脑网线连接不上网络怎么回事排查与解决教程

    电脑插上网线却显示无网络?本教程带你从物理接口、网卡状态到系统设置逐一排查,解决因接触不良、IP配置错误或驱动异常导致的有线网络中断问题。

    Ubuntu系统中怎么设置IP地址?
    Ubuntu系统中怎么设置IP地址?

    如何在Ubuntu系统中轻松配置有线网络IP地址 从Windows转向Ubuntu系统时,第一步往往是让新系统顺利联网。这一步很关键——不仅能方便地安装各类软件,还能随时获取系统更新,让整个使用体验流畅起来。那么,如何在Ubuntu下高效地设置有线网络的IP地址呢?这套操作流程同样能帮你恢复自动获取

    Win11查看IP地址及网络信息方法
    Win11查看IP地址及网络信息方法

    Windows11中查看以太网IPv4地址有五种方法:一、设置→网络和Internet→以太网→IP分配区;二、任务栏网络图标右键→网络和Internet设置→连接属性;三、cmd中运行ipconfig;四、PowerShell中运行Get-NetIPAddress筛选;五、ncpa.cpl→以太网状态→详细信息。

    Win11查看IP地址方法:ipconfig与网络设置教程
    Win11查看IP地址方法:ipconfig与网络设置教程

    1、通过命令提示符输入ipconfig可查看IPv4地址;2、设置中进入网络属性可直接查看IP分配信息;3、控制面板的网络共享中心详细信息中也显示IPv4地址。

    PHP获取用户真实IP的正确方法
    PHP获取用户真实IP的正确方法

    答案:在PHP中,$_SERVER['REMOTE_ADDR']不总是真实IP,因为当请求经过CDN、负载均衡或反向代理时,该值仅为代理服务器IP。真实客户端IP通常由代理服务器通过HTTP头如X-Forwarded-For或X-Real-IP传递。为获取可信IP,应优先解析这些头信息,并结合信任代理IP范围和IP有效性验证(如过滤私有IP),防止IP欺骗。最安全的做法是在Web服务器(如Nginx)层面配置real_ip_header和set_real_ip_from,将真实IP写入REMOTE_ADD

    PHP获取客户端IP地址的正确方法
    PHP获取客户端IP地址的正确方法

    答案:PHP获取客户端IP需综合$_SERVER['REMOTE_ADDR']、$_SERVER['HTTP_X_FORWARDED_FOR']等变量,优先取代理头中的第一个IP并验证有效性。应结合filter_var()校验IP格式,区分IPv4/IPv6,仅信任可信代理,记录多源IP以备分析,无单一最佳方案,需根据服务器环境调整策略。

    Win8查看本机IP地址方法
    Win8查看本机IP地址方法

    首先通过命令提示符输入ipconfig查询IP地址,其次可通过网络共享中心查看连接详情,最后在系统设置中直接访问网络状态获取IPv4地址。

    网络与IP地址有何关联?
    网络与IP地址有何关联?

    IP地址是设备在网络中的唯一标识,如同门牌号,而网络则是其归属的空间;两者相辅相成,缺一不可。

    如何在 Go 中用正则表达式匹配 IP 地址?
    如何在 Go 中用正则表达式匹配 IP 地址?

    如何在Go中使用正则表达式匹配IP地址?正则表达式语法:^(([0-9]|1-9|1[0-9]{2}|20-4|25[0-5]).){3}([0-9]|1-9|1[0-9]{2}|20-4|25[0-5])$Go代码示例:使用regexp.MatchString()函数进行匹配。实战案例:使用regexp.MustCompile()函数从字符串中替换所有IP地址。

    如何在 PHP 中使用正则表达式来匹配 Apache 访问日志中的 IP 地址
    如何在 PHP 中使用正则表达式来匹配 Apache 访问日志中的 IP 地址

    Apache访问日志是Apache服务器记录客户端请求和服务器响应的一种形式。该日志中包含了每个请求的详细信息,包括客户端的IP地址、请求的时间、请求的URL地址等信息。在PHP开发过程中,我们有时需要从访问日志中获取客户端的IP地址。为此,我们可以使用PHP中的正则表达式进行匹配。以下是如何在PHP中使用正则表达式来匹配Apache访问日志中的IP地

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。