首頁 > 軟體

Java根據ip地址獲取歸屬地範例詳解

2022-08-30 18:02:37

引言

最近,各大平臺都新增了評論區顯示發言者ip歸屬地的功能,例如嗶哩嗶哩,微博,知乎等等。

Java 中是如何獲取 IP 屬地的

主要分為以下幾步

  • 通過 HttpServletRequest 物件,獲取使用者的 IP 地址
  • 通過 IP 地址,獲取對應的省份、城市

首先需要寫一個 IP 獲取的工具類

因為每一次使用者的 Request 請求,都會攜帶上請求的 IP 地址放到請求頭中。

public class IpUtils {
    /**
     * 獲取ip地址
     * @param request
     * @return
     */
    public static String getIpAddr(HttpServletRequest request){
        String ipAddress = null;
        try {
            ipAddress = request.getHeader("X-Forwarded-For");
            if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
                // 多次反向代理後會有多個ip值,第一個ip才是真實ip
                if (ipAddress.indexOf(",") != -1) {
                    ipAddress = ipAddress.split(",")[0];
                }
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("HTTP_CLIENT_IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
            }
        }catch (Exception e) {
            log.error("IPUtils ERROR ",e);
        }
        return ipAddress;
    }

對這裡出現的幾個名詞解釋一下:

  • X-Forwarded-For:一個 HTTP 擴充套件頭部,主要是為了讓 Web 伺服器獲取存取使用者的真實 IP 地址。每個 IP 地址,每個值通過逗號+空格分開,最左邊是最原始使用者端的 IP 地址,中間如果有多層代理,每⼀層代理會將連線它的使用者端 IP 追加在 X-Forwarded-For 右邊。
  • Proxy-Client-IP:這個一般是經過 Apache http 伺服器的請求才會有,用 Apache http 做代理時一般會加上 Proxy-Client-IP 請求頭
  • WL-Proxy-Client-IP:也是通過 Apache http 伺服器,在 weblogic 外掛加上的頭。
  • X-Real-IP:一般只記錄真實發出請求的使用者端IP
  • HTTP_CLIENT_IP:代理伺服器傳送的HTTP頭。如果是“超級匿名代理”,則返回none值。

這裡,要著重介紹一下Ip2region專案。

github地址:github.com/lionsoul201…

一個準確率 99.9% 的離線 IP 地址定位庫,0.0x 毫秒級查詢,ip2region.db 資料庫只有數MB,提供了 java,php,c,python,nodejs,golang,c# 等查詢繫結和Binary,B樹,記憶體三種查詢演演算法。

內建的三種查詢演演算法

全部的查詢使用者端單次查詢都在 0.x 毫秒級別,內建了三種查詢演演算法

  • memory 演演算法:整個資料庫全部載入記憶體,單次查詢都在0.1x毫秒內,C語言的使用者端單次查詢在0.00x毫秒級別。
  • binary 演演算法:基於二分查詢,基於ip2region.db檔案,不需要載入記憶體,單次查詢在0.x毫秒級別。
  • b-tree 演演算法:基於btree演演算法,基於ip2region.db檔案,不需要載入記憶體,單詞查詢在0.x毫秒級別,比binary演演算法更快。

使用方法

1、引入ip2region依賴

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>1.7.2</version>
</dependency>

2、下載倉庫中的ip2region.db 檔案,放到工程resources目錄下

3、編寫方法載入ip2region檔案,對使用者ip地址進行轉換。

/**
 * 獲取ip屬地
 * @param ip
 * @return
 * @throws Exception
 */
public static String getCityInfo(String ip) throws Exception {
    //獲得檔案流時,因為讀取的檔案是在打好jar檔案裡面,不能直接通過檔案資源路徑拿到檔案,但是可以在jar包中拿到檔案流
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource[] resources = resolver.getResources("ip2region.db");
    Resource resource = resources[0];
    InputStream is = resource.getInputStream();
    File target = new File("ip2region.db");
    FileUtils.copyInputStreamToFile(is, target);
    is.close();
    if (StringUtils.isEmpty(String.valueOf(target))) {
        log.error("Error: Invalid ip2region.db file");
        return null;
    }
    DbConfig config = new DbConfig();
    DbSearcher searcher = new DbSearcher(config, String.valueOf(target));
    //查詢演演算法
    //B-tree, B樹搜尋(更快)
    int algorithm = DbSearcher.BTREE_ALGORITHM;
    try {
        //define the method
        Method method;
        method = searcher.getClass().getMethod("btreeSearch", String.class);
        DataBlock dataBlock;
        if (!Util.isIpAddress(ip)) {
            log.error("Error: Invalid ip address");
        }
        dataBlock = (DataBlock) method.invoke(searcher, ip);
        String ipInfo = dataBlock.getRegion();
        if (!StringUtils.isEmpty(ipInfo)) {
            ipInfo = ipInfo.replace("|0", "");
            ipInfo = ipInfo.replace("0|", "");
        }
        return ipInfo;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

4、由於 ip 屬地在國內的話,只會展示省份,而國外的話,只會展示國家。所以我們還需要對這個方法進行一下封裝,得到獲取 IP 屬地的資訊。

public static String getIpPossession(String ip) throws Exception {
    String cityInfo = IpUtils.getCityInfo(ip);
    if (!StringUtils.isEmpty(cityInfo)) {
        cityInfo = cityInfo.replace("|", " ");
        String[] cityList = cityInfo.split(" ");
        if (cityList.length > 0) {
            // 國內的顯示到具體的省
            if ("中國".equals(cityList[0])) {
                if (cityList.length > 1) {
                    return cityList[1];
                }
            }
            // 國外顯示到國家
            return cityList[0];
        }
    }
    return "未知";
}

5、編寫測試類。

public static void main(String[] args) throws Exception {
    //國內ip
    String ip1 = "220.248.12.158";
    String cityInfo1 = IpUtils.getCityInfo(ip1);
    System.out.println(cityInfo1);
    String address1 = IpUtils.getIpPossession(ip1);
    System.out.println(address1);
    //國外ip
    String ip2 = "67.220.90.13";
    String cityInfo2 = IpUtils.getCityInfo(ip2);
    System.out.println(cityInfo2);
    String address2 = IpUtils.getIpPossession(ip2);
    System.out.println(address2);
}

6、測試結果

專案用到的全部依賴

想了解的小夥伴可以學習一下!

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.36</version>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
</dependency>
<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>1.7.2</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>servlet-api</artifactId>
    <version>2.5</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

以上就是Java根據ip地址獲取歸屬地範例詳解的詳細內容,更多關於Java根據ip獲取歸屬地的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com