手把手教你用Java获取IP归属地

Java
217
0
0
2023-06-16

前几个月微信公众号上线了 Ip 归属地 的功能,后续知乎、 抖音 等平台纷纷添加了该功能。如果是国内的用户精确到省份,国外用户精确到国家。本文就使用 Java 实现获取 IP归属地

主要讲解几个步骤:

  • Java 获取请求 IP
  • 解决 Nginx 转发问题
  • 通过 IP 地址获取归属地

获取IP地址

首先使用基于 Spring Boot 搭建项目,在 controller 添加 HttpServlet Request 请求参数:

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

通过 HttpServletRequest 获取 IP地址

 String ip = request.get Header ("x-forwarded-for");
if (ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
    ip = request.getHeader("Proxy-Client-IP");
}
if (ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
    ip = request.getHeader("WL-Proxy-Client-IP");
}
if (ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
    ip = request.getHeader("HTTP_CLIENT_IP");
}
if (ip == null || ip.length() == || "unknown".equalsIgnoreCase(ip)) {
    ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (ip == null || ip.length() == || "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.1 IP

所以需要部署到 外网服务器 才能获取到公网地址。部署到外网的服务器能成功获取 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;  
    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 
    } 

完成以上操作之后,就能成功获取到 IP 了。然后通过 IP 获取归属地了。

IP获取归属地

通过 IP 获取归属地一般都是从地址库找到匹配的地址,本文介绍两种方法.

通过归属地API获取

需要发起 http 请求,这里使用 Spring Boot Rest Template 发起 http 请求,首先创建 RestTemplate bean 实例:

 @Configuration
public class RestTemplateConfig {

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

再调用 RestTemplate 发起 http 请求:

 private String URL = "#;;
 json Object jsonObject = new JSONObject();
jsonObject.put("ip",ip);
 JSON Object json = restTemplate.postForObject(URL,jsonObject, JSONObject.class);
if (json.getInteger("code") ==) {
    json = json.getJSONObject("data");
    // 国家
    String nation = json.getString("nation");
    // 省份
    String province = json.getString("province");
    // 市
    String city = json.getString("city");
} 

上面的 json 是引入 fastjson

通过地址库获取

使用 API接口 ,可能会出现服务挂了,或者服务地址不提供服务了等问题。而采用本地地址库就没有这些问题。

本文采用离线 IP 地址定位库 Ip2region , Ip2region 是一个离线 IP 地址定位库, 微秒 的查询时间:

首先找到在 gihub官网 找到地址库 ip2region.xdb ,具体路径为 data/ip2region.xdb :

ip2region.xdb 放在项目的 resources 目录下:

引入 maven 依赖:

 <dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ipregion</artifactId>
<version>.6.5</version>
</dependency> 

获取归属地:

  private  Searcher searcher;

@Override
    public String getIpAddress(String ip){
        if (".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 (FileNotFound Exception  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() >) {
                errorMessage = errorMessage.substring(,256);
            }
            e.printStackTrace();
        }
        // 输出 region
    } 

获取 region 就能获取到 IP 归属地了。例如 中国|0|广东省|广州市|电信