• Java IP归属地查询(离线方式+在线方式,内附查询IP方法)


    一、离线方式

    1.1. 下载 ip2region.xdb

    GitHub项目地址:https://github.com/lionsoul2014/ip2region
    我们首先需要下载一个 ip2region.xdb 的文件
    下载地址:https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb
    打开后点击如图的Download图标即可下载。
    在这里插入图片描述
    下载完成后,需要将该文件放到我们的项目中。
    ps:我是直接放到服务器的,因为放在项目的资源文件夹下,当我们调试的时候使用Java Spring自带的工具去获取该文件的绝对路径时,没有任何问题,能够正常获取到。但是当我们打成jar包部署到测试或者开发环境,就会拿不到该文件,具体情况可以看 spring boot集成,db放在resource下,打jar包后可以加载资源,但search不生效。本地debug生效 该Issues,如果不想麻烦的话直接放在服务器上吧,这样我们就能直接配好绝对路径了,少走弯路。

    1.2. 代码实现

    首先需要导包:

    	<dependency>
    	    <groupId>org.lionsoulgroupId>
    	    <artifactId>ip2regionartifactId>
    	    <version>2.6.5version>
    	dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    	/**
         * 通过ip地址获取所属国家-离线方式
         *
         * @param ip        IP地址
         * @param dbPath    ip2region.xdb路径
         * @return      国家信息
         */
    	public static String getCountryByIpOffline(String ip, String dbPath) throws Exception{
            // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。
            byte[] vIndex = Searcher.loadVectorIndexFromFile(dbPath);
            // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
            Searcher searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
            // 3、查询
            return searcher.search(ip);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    其中dbPath我是走的nacos配置,这个路径就是我们服务器存放ip2region.xdb的路径。

    1.3. 查询结果解析

    ip2region xdb 数据结构和查询过程的详解:ip2region xdb 数据结构和查询过程的详解
    这篇文章写得很清楚我就不再叙述了。
    ps:需要特别提一点的是,该方式查询国内的IP还是比较准确的,基本上能精确到省市。但是国外的一些IP,大体的国家信息没啥问题,再具体的信息可能就不全了。但是一般项目都够用。

    查询结果展示:
    在这里插入图片描述

    查询结果统一采用的是 国家|区域|省份|城市|ISP 的方式返回。

    二、在线方式

    2.1 接口地址

    在线方式网页测试:在线方式网页测试

    在这里插入图片描述
    只需要通过GET请求该链接 http://ip-api.com/json/ 并后面拼接你的ip,如 http://ip-api.com/json/24.48.0.1 即可。
    也能通过添加lang参数让该接口返回中文。如 http://ip-api.com/json/24.48.0.1?lang=zh-CN,就可以返回中文数据了。

    2.2 代码实现

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.ResponseEntity;
    import org.springframework.stereotype.Component;
    import org.springframework.web.client.RestTemplate;
    import javax.annotation.PostConstruct;
    
    @Slf4j
    @Component
    public class IpUtil {
    
        private static RestTemplate restTemplate;
    
        private final RestTemplate template;
    
        public static final String IP_API_URL = "http://ip-api.com/json/";
    
        public IpUtil(RestTemplate restTemplate) {
            this.template = restTemplate;
        }
    
        /**
         * 初始化 RestTemplate
         */
        @PostConstruct
        public void init() {
            setRestTemplate(this.template);
        }
    
        /**
         * 初始化 RestTemplate
         */
        private static void setRestTemplate(RestTemplate template) {
            restTemplate = template;
        }
    
        /**
         * 通过ip地址获取所属国家-在线方式
         *
         * @param ip    ip地址
         * @return      国家信息
         */
        public static CountryInfo getCountryByIpOnline(String ip) {
            ResponseEntity<CountryInfo> entity = restTemplate.getForEntity(
                    IP_API_URL + ip + "?lang=zh-CN",
                    CountryInfo.class
            );
            return entity.getBody();
        }
    
        /**
         * 在线方式接收类
         */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public static class CountryInfo {
            private String query;
            private String status;
            private String country;
            private String countryCode;
            private String region;
            private String regionName;
            private String city;
            private String zip;
            private String lat;
            private String lon;
            private String timezone;
            private String isp;
            private String org;
            private String as;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    代码也非常简单。

    三、使用建议

    这里我推荐我们项目中都统一采用先离线后在线方式兜底的写法来处理。因为谁也不能保证离线的就一定能查询到具体的归属地,如果我们使用离线方式查询不到具体的归属地(获取结果为null),那么我建议再使用在线查询的方式来做一个兜底处理,如果都拿不到,再返回空,或者根据自己的业务具体处理。
    以上仅作为建议。

    四、获取请求IP地址

    这里附带一下查询请求的ip地址方法,有需要的可以直接拿走。

    /**
     * 获取Ip地址
     *
     * @author dxc
     * @date 2022/7/12 18:18
     */
    public final class IpUtil {
    
        private static final String UNKNOWN = "unknown";
    
        private IpUtil() {}
    
        /**
         * 获取客户端ip 地址
         *
         * @param request 请求
         * @return {@link String}
         */
        public static String getClientIpAddr(HttpServletRequest request) {
            String ip = request.getHeader("X-Forwarded-For");
    
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_FORWARDED_FOR");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_FORWARDED");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_VIA");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getHeader("REMOTE_ADDR");
            }
            if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
    
            // 多次反向代理后会有多个ip值,第一个ip才是真实ip
            int index = ip.indexOf(",");
            if (index != -1) {
                return ip.substring(0, index);
            } else {
                return ip;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
  • 相关阅读:
    PFL 1.2.0版本 使用docker镜像编译流程
    RabbitMQ多线程配置和异常解决办法
    计算机毕业设计之java+SSM酒店客房预定管理系统
    零基础入门MATLAB(一篇十分钟)
    科大讯飞发布讯飞星火 3.0;开源AI的现状
    简单排序算法、二分法以及对数器
    基于AT89C51单片机与DS18B20的温度测量系统
    人工智能基础_机器学习037_多项式回归升维实战4_使用随机梯度下降模型_对天猫双十一销量数据进行预测_拟合---人工智能工作笔记0077
    MyBatis学习(二):补充
    基于深度学习的视觉目标检测技术综述
  • 原文地址:https://blog.csdn.net/weixin_44248000/article/details/127691768