全網都在顯示的IP歸屬地,5分鐘帶你加上,就這麼簡單

ehang發表於2023-11-19
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 離線庫

官網地址:

下載過程稍微有點點麻煩,這裡下載了一份最新的,放在網盤,需要測試的可以直接透過這個連結下載:

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地址;

百度

地址:

響應資料:

{

   "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資訊

  • 指定國際化

    ?lang=zh-CN

  • 指定IP查詢

    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查詢

返回資料比較的簡單:

var returnCitySN = {
"cip""xxx.xxx.xxx.xxx""cid""440300""cname""廣東省深圳市"};

太平洋IP地址查詢

地址:

返回資料:

{

   "ip""183.17.xxx.138",
   "pro""廣東省",
   "proCode""440000",
   "city""深圳市",
   "cityCode""440300",
   "region""",
   "regionCode""0",
   "addr""廣東省深圳市 電信",
   "regionNames""",
   "err"""
}

淘寶API介面

{

     "code"0,
     "data": {
         "ip""183.17.xxx.138",
         "country""中國",
         "area""",
         "region""廣東",
         "city""深圳",
         "county""XX",
         "isp""電信"
    }
}

code等於0表示成功,1表示失敗

126

地址:

響應資料:


var lo=
"廣東省", lc=
"肇慶市"
var localAddress={ city: "肇慶市"province: "廣東省"}

響應的資料比較的簡單

IP資訊

地址:

響應資料:

{

   "country""中國",
   "short_name""CN",
   "province""廣東省",
   "city""肇慶市",
   "area""德慶縣",
   "isp""電信",
   "net""",
   "ip""183.19.xxx.138",
   "code"200,
   "desc""success"
}

這麼多的姿勢,實現起來是不是就非常的容易了;如果你對IP解析的需求比較依賴,也完全可以透過離線加這麼多線上的方式,開發一個單獨的IP解析模組,作為公司的基礎服務,提供給內部其他模組使用。

好了,今天的分享就到這裡,感謝你的點贊、關注、收藏!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70035356/viewspace-2996036/,如需轉載,請註明出處,否則將追究法律責任。

相關文章