ITPub博客

首页 > 应用开发 > Java > 全网都在显示的IP归属地,5分钟带你加上,就这么简单

全网都在显示的IP归属地,5分钟带你加上,就这么简单

原创 Java 作者:ehang 时间:2023-11-19 11:25:47 0 删除 编辑
image-20220515220102985 如遇图片加载失败,可尝试使用手机流量访问image-20220515220102985 如遇图片加载失败,可尝试使用手机流量访问

大家好,我是一航!

最近,继新浪微博之后,今日头条、腾讯、抖音、知乎、快手、小红书、百家号等各大平台陆陆续续都上线了" 网络用户IP地址显示功能",境外用户显示的是国家,国内的用户显示的省份,而且此项显示无法关闭,归属地强制显示;

作为技术人,那!这个功能要怎么实现呢?

其实要想实现这个功能还是非常的容易,基于现成 GeoLite2离线库+免费的在线解析资源,5分钟就能整合了;

在整合之前,我们先简单了解一下,要想拿到用户的位置信息,有那些方式:

  • 终端定位

    我们的手机等电子设备都是带有GPS定位功能的,APP可以申请权限获取用户所处的经纬度坐标,根据坐标,就可以知道到用户所处的位置;比如百度、高德等地图厂商,就提供了完善的SDK,能非常方便的集成到应用,快速根据经纬度获取详细的位置详细;

    优点

    • 快捷;
    • 准确;
    • 误差小。

    缺点

    • 依赖硬件支持;
    • 依赖用户授权,如果用户不授权,APP将拿不到经纬度信息,导致失败;
  • IP地址获取

    用户向服务端发起的请求都会带上IP地址,服务端拿到IP地址后,就能基于IP解析出用户的所处的位置;

    优点

    • 无需授权,只要用户跟服务端交互,服务端就能拿到对应的IP信息

    缺点

    • 准确性不高,位置可能存在偏差;
    • IP库更新不及时,导致部分IP归属地解析失败。
  • 三方终端上报

    比如,我们骑共享单车的时候,我们的位置信息就是通过单车的设备上报到服务器;

    优点

    • 由三方终端基于GPS定位上报,不会获取个人设备的信息;
    • 准确快捷;
    • 专业设备,误差小;

    缺点

    • 用户无法干预,信息会被迫强制上传至服务端,用户无法取消上传;

下面就来试着将 GeoLite2 免费 IP 库整合值SpringBoot项目,来获取用户的归属地信息;

什么是GeoLite2?

GeoLite2数据库是免费的IP地理定位数据库;

优点:

  • 离线库,不需要网络
  • 数据库丰富
  • 速度快
  • 免费

缺点:

  • 准确度不高,存在偏差
  • 数据更新慢

下载 GeoLite2 离线库

官网地址:https://www.maxmind.com/en/home

下载过程稍微有点点麻烦,这里下载了一份最新的,放在网盘,需要测试的可以直接通过这个链接下载:https://www.123pan.com/s/xPY9-J37vH

SpringBoot 获取用户的IP

  • 工具类

    
    public 
    
    class 
    IpUtils {
    
         /**
         * 获取用户IP
         *  @param request
         *  @return
         */

         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( "X-Real-IP");
            }
             if (ip ==  null || ip.length() ==  0 ||  "unknown".equalsIgnoreCase(ip)) {
                ip = request.getHeader( "http_client_ip");
            }
             if (ip ==  null || ip.length() ==  0 ||  "unknown".equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
             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.getHeader( "HTTP_X_FORWARDED_FOR");
            }

             // 如果是多级代理,那么取第一个ip为客户ip
             if (ip !=  null && ip.indexOf( ",") != - 1) {
                ip = ip.substring(ip.lastIndexOf( ",") +  1).trim();
            }
             return ip;
        }
    }
  • Controller获取HttpServletRequest

    通过上面的工具类,即可获取用户请求的真实IP;

    为了避免重复工作,这里也可以使用AOP解析出用户的IP信息,放到用户的请求对象中

    
    @RestController
    
    public  class  IpController {
         @GetMapping( "/user/ip")
         public String  userIp (HttpServletRequest request) {
             // 这里就能拿到用户的真实IP
             return IpUtils.getIpAddr(request);
        }
    }

SpringBoot 整合 GeoLite2

  • 添加依赖

    
    <
    dependency>
    
         < groupId>com.maxmind.geoip2 </ groupId>
         < artifactId>geoip2 </ artifactId>
         < version>2.3.0 </ version>
    </ dependency>

    < dependency>
         < groupId>com.maxmind.db </ groupId>
         < artifactId>maxmind-db </ artifactId>
         < version>1.0.0 </ version>
    </ dependency>
  • 工具类

    
    public 
    
    class 
    GeoIpUtils {
    
         private  static DatabaseReader reader;

         private  static  void  init () {
             try {
                 // 创建 GeoLite2 数据库 Reader
                 // 这里可以放在本地磁盘,也可以随项目放在resource目录下
                File database =  new File( "F:\\web\\GeoLite2-City.mmdb");
                 // 读取数据库内容
                reader =  new DatabaseReader.Builder(database).build();
            }  catch (Exception ex) {

            }
        }

         public  static  void  getCityByIP (String ip)  throws Exception {
             if ( null == reader) {
                init();
            }

            InetAddress ipAddress = InetAddress.getByName(ip);

             // 获取查询结果
            CityResponse response = reader.city(ipAddress);

             // 获取国家信息
            Country country = response.getCountry();
            System.out.println( "国家信息:" + JSON.toJSONString(country));

             // 获取省份
            Subdivision subdivision = response.getMostSpecificSubdivision();
            System.out.println( "省份信息:" + JSON.toJSONString(subdivision));

             //城市
            City city = response.getCity();
            System.out.println( "城市信息:" + JSON.toJSONString(city));

             // 获取城市
            Location location = response.getLocation();
            System.out.println( "经纬度信息:" + JSON.toJSONString(location));
        }
    }
  • 测试

    
    
    public 
    static 
    void 
    main
    (String[] args) 
    throws Exception {
    
        String ip =  "183.19.xxx.138";
        GeoIpUtils.getCityByIP(ip);
    }

    输出结果:

    国家信息:{
    "geoNameId":
    1814991,
    "isoCode":
    "CN",
    "name":
    "China",
    "names":{
    "de":
    "China",
    "ru":
    "Китай",
    "pt-BR":
    "China",
    "ja":
    "中国",
    "en":
    "China",
    "fr":
    "Chine",
    "zh-CN":
    "中国",
    "es":
    "China"}}
    
    省份信息:{ "geoNameId": 1809935, "isoCode": "GD", "name": "Guangdong", "names":{ "en": "Guangdong", "fr": "Province de Guangdong", "zh-CN": "广东"}}
    城市信息:{ "geoNameId": 1998011, "name": "Yanqianlaocun", "names":{ "en": "Yanqianlaocun", "zh-CN": "岩前老村"}}
    经纬度信息:{ "accuracyRadius": 500, "latitude": 23.3255, "longitude": 116.5007, "timeZone": "Asia/Shanghai"}

就这么简单,轻轻松松就能拿到用户IP所处的国家、省份、城市、经纬度等详细信息,可以根据自己的业务需要,对这些数据再做进一步的封装。

GeoLite2的其他用法

上面介绍的时SpringBoot整合GeoLite2,同样在其他的一些场景下,也是可以利用GeoLite2获取归属地信息;

  • 整合至Nignx,获取用户归属地信息

    Nginx 整合 GeoLite2 来解析用户的归属地信息,在代理层就直接整理好对应的数据;

  • ELK中整合GeoLite2

    ELK 日志整理的时候,可以通过GeoLite2 获取用户的IP归属地信息;然后通过Kibana,就能非常直观的展示用户的地域分布情况;

在线方案

上面一开始介绍GeoLite2时就列举了其离线库更新收录不及时的问题,可能导致一些IP在离线库中并不存在,查找的时候,就会报 AddressNotFoundException的错误,如下示例:

 如遇图片加载失败,可尝试使用手机流量访问如遇图片加载失败,可尝试使用手机流量访问

遇到这种请求,我们要怎么办呢?

下面就来介绍几种在线IP归属地获取的方式,当本地离线库无法获取的时候,就可以利用三方的在线库,来补充完善;

在线获取的优点:

  • IP更新及时
  • 准确度高

缺点

  • 三方依赖性强
  • 需要付费,免费版本一般都有各种限制

以下示例中的xxx.xxx.xxx.xxx均代表ip地址;

百度

地址:https://opendata.baidu.com/api.php?query=xxx.xxx.xxx.xxx&resource_id=6006&co=&oe=utf8

响应数据:

{

   "status":  "0",
   "t":  "",
   "set_cache_time":  "",
   "data": [
    {
       "ExtendedLocation":  "",
       "OriginQuery":  "183.19.xxx.138",
       "appinfo":  "",
       "disp_type":  0,
       "fetchkey":  "183.19.xxx.138",
       "location":  "广东省肇庆市 电信",
       "origip":  "183.19.xxx.138",
       "origipquery":  "183.19.xxx.138",
       "resourceid":  "6006",
       "role_id":  0,
       "shareImage":  1,
       "showLikeShare":  1,
       "showlamp":  "1",
       "titlecont":  "IP地址查询",
       "tplt":  "ip"
    }
  ]
}

status等于0表示成功,1表示失败;可能存在status等于0,但是data中没有数据的情况。

ip-api接口

  • 本机的IP信息

    http://ip-api.com/json/

  • 指定国际化

    http://ip-api.com/json/?lang=zh-CN

  • 指定IP查询

    http://ip-api.com/json/xxx.xxx.xxx.xxx?lang=zh-CN

    返回数据:

    {
    
       "status":  "success",
       "country":  "中国",
       "countryCode":  "CN",
       "region":  "GD",
       "regionName":  "广东",
       "city":  "岩前老村",
       "zip":  "",
       "lat":  23.3255,
       "lon":  116.5007,
       "timezone":  "Asia/Shanghai",
       "isp":  "Chinanet",
       "org":  "Chinanet GD",
       "as":  "AS4134 CHINANET-BACKBONE",
       "query":  "183.19.xxx.138"
    }

搜狐IP查询

http://pv.sohu.com/cityjson?ie=utf-8

返回数据比较的简单:

var returnCitySN = {
"cip": 
"xxx.xxx.xxx.xxx", 
"cid": 
"440300", 
"cname": 
"广东省深圳市"};

太平洋IP地址查询

地址:http://whois.pconline.com.cn/ipJson.jsp?ip=xxx.xxx.xxx.xxx&json=true

返回数据:

{

   "ip":  "183.17.xxx.138",
   "pro":  "广东省",
   "proCode":  "440000",
   "city":  "深圳市",
   "cityCode":  "440300",
   "region":  "",
   "regionCode":  "0",
   "addr":  "广东省深圳市 电信",
   "regionNames":  "",
   "err":  ""
}

淘宝API接口

http://ip.taobao.com/service/getIpInfo.php?ip=xxx.xxx.xxx.xxx

{

     "code":  0,
     "data": {
         "ip":  "183.17.xxx.138",
         "country":  "中国",
         "area":  "",
         "region":  "广东",
         "city":  "深圳",
         "county":  "XX",
         "isp":  "电信"
    }
}

code等于0表示成功,1表示失败

126

地址:https://ip.ws.126.net/ipquery?ip=xxx.xxx.xxx.xxx

响应数据:


var lo=
"广东省", lc=
"肇庆市"; 

var localAddress={ city: "肇庆市",  province: "广东省"}

响应的数据比较的简单

IP信息

地址:https://ip.useragentinfo.com/json?ip=xxx.xxx.xxx.xxx

响应数据:

{

   "country":  "中国",
   "short_name":  "CN",
   "province":  "广东省",
   "city":  "肇庆市",
   "area":  "德庆县",
   "isp":  "电信",
   "net":  "",
   "ip":  "183.19.xxx.138",
   "code":  200,
   "desc":  "success"
}

这么多的姿势,实现起来是不是就非常的容易了;如果你对IP解析的需求比较依赖,也完全可以通过离线加这么多在线的方式,开发一个单独的IP解析模块,作为公司的基础服务,提供给内部其他模块使用。

好了,今天的分享就到这里,感谢你的点赞、关注、收藏!


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70035356/viewspace-2996036/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2023-11-17

  • 博文量
    14
  • 访问量
    22106