手把手教你用Java獲取IP歸屬地

2022-09-12 06:00:26

前幾個月微信公眾號上線了IP歸屬地的功能,後續知乎、抖音等平臺紛紛新增了該功能。如果是國內的使用者精確到省份,國外使用者精確到國家。本文就使用Java實現獲取IP歸屬地

!

主要講解幾個步驟:

  • Java獲取請求IP
  • 解決Nginx轉發問題
  • 通過IP地址獲取歸屬地

獲取IP地址

首先使用基於Spring Boot搭建專案,在controller新增HttpServletRequest請求引數:

@RestController
public class IpController {
    @GetMapping("/ip-address")
    public String ipAddress(HttpServletRequest request)  {
        // 接收request  
    }
}

通過HttpServletRequest獲取IP地址

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

在本地環境呼叫獲取IP,要麼是0:0:0:0:0:0:0:1,或者是區域網IP

區域網IP是以192.168.x.x開頭,或者是127.0.0.1IP

所以需要部署到外網伺服器才能獲取到公網地址。部署到外網伺服器能成功獲取IP地址。

Nginx 反向代理問題

直接存取公網伺服器地址能成功獲取IP地址,但是通過Nginx反向代理獲取的都是127.0.0.1。使用者端請求Nginx伺服器再反向代理轉發到伺服器端,此時拿到的IP反向代理的IP,也就是Nginx伺服器的IP,並不是真正的使用者端IP

Nginx的組態檔中的location模組新增以下設定,將使用者端的IP傳入到Nginx服務:

proxy_set_header        X-Real-IP       $remote_addr;
proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;

範例:

server {  
    listen 80;  
    server_name localhost;  
    location / { 
         proxy_set_header        X-Real-IP       $remote_addr;
         proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
         proxy_pass http://xxxx;
    }

完成以上操作之後,就能成功獲取到IP了。然後通過IP獲取歸屬地了。

IP獲取歸屬地

通過IP獲取歸屬地一般都從地址庫找到匹配的地址,本文介紹兩種方法.

通過歸屬地API獲取

需要發起http請求,這裡使用Spring BootRestTemplate發起http請求,首先建立RestTemplatebean範例:

@Configuration
public class RestTemplateConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

再呼叫RestTemplate發起http請求:

private String URL = "https://api.beijinxuetang.com/api/common/ip";
JSONObject jsonObject = new JSONObject();
jsonObject.put("ip",ip);
JSONObject json = restTemplate.postForObject(URL,jsonObject, JSONObject.class);
if (json.getInteger("code") == 0) {
    json = json.getJSONObject("data");
    // 國家
    String nation = json.getString("nation");
    // 省份
    String province = json.getString("province");
    // 市
    String city = json.getString("city");
}

上面的json是引入fastjson

通過地址庫獲取

使用API介面,可能會出現服務掛了,或者服務地址不提供服務了等問題。而採用本地地址庫就沒有這些問題。

本文采用離線IP地址定位庫 Ip2regionIp2region是一個離線IP地址定位庫,微秒的查詢時間:

首先找到在gihub官網找到地址庫ip2region.xdb,具體路徑為data/ip2region.xdb:

ip2region.xdb放在專案的resources目錄下:

引入maven依賴:

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

獲取歸屬地:

private Searcher searcher;

@Override
    public String getIpAddress(String ip){
        if ("127.0.0.1".equals(ip) || ip.startsWith("192.168")) {
            return "區域網 ip";
        }
        if (searcher == null) {
            try {
                File file = ResourceUtils.getFile("classpath:ipdb/ip2region.xdb");
                String dbPath = file.getPath();
                searcher = Searcher.newWithFileOnly(dbPath);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        String region = null;
        String errorMessage = null;
        try {
            region = searcher.search(ip);
        } catch (Exception e) {
            errorMessage = e.getMessage();
            if (errorMessage != null && errorMessage.length() > 256) {
                errorMessage = errorMessage.substring(0,256);
            }
            e.printStackTrace();
        }
        // 輸出 region
    }

獲取region就能獲取到IP歸屬地了。例如中國|0|廣東省|廣州市|電信

小程式效果展示

根據上面的程式,做了一個小程式展示歸屬地。

頁面效果圖:

掃一掃,就能獲取查到自己的歸屬地了。