Java IP归属地查询(离线方式+在线方式,内附查询IP方法)

一、离线方式

1.1. 下载 ip2region.xdb

GitHub项目地址:https://github.com/lionsoul2014/ip2region
我们首先需要下载一个 ip2region.xdb 的文件
下载地址:https://github.com/lionsoul2014/ip2region/blob/master/data/ip2region.xdb
打开后点击如图的Download图标即可下载。
在这里插入图片描述
下载完成后,需要将该文件放到我们的项目中。
ps:我是直接放到服务器的,因为放在项目的资源文件夹下,当我们调试的时候使用Java Spring自带的工具去获取该文件的绝对路径时,没有任何问题,能够正常获取到。但是当我们打成jar包部署到测试或者开发环境,就会拿不到该文件,具体情况可以看 spring boot集成,db放在resource下,打jar包后可以加载资源,但search不生效。本地debug生效 该Issues,如果不想麻烦的话直接放在服务器上吧,这样我们就能直接配好绝对路径了,少走弯路。

1.2. 代码实现

首先需要导包:

	<dependency>
	    <groupId>org.lionsoul</groupId>
	    <artifactId>ip2region</artifactId>
	    <version>2.6.5</version>
	</dependency>
	/**
     * 通过ip地址获取所属国家-离线方式
     *
     * @param ip        IP地址
     * @param dbPath    ip2region.xdb路径
     * @return      国家信息
     */
	public static String getCountryByIpOffline(String ip, String dbPath) throws Exception{
        // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。
        byte[] vIndex = Searcher.loadVectorIndexFromFile(dbPath);
        // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
        Searcher searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
        // 3、查询
        return searcher.search(ip);
    }

其中dbPath我是走的nacos配置,这个路径就是我们服务器存放ip2region.xdb的路径。

1.3. 查询结果解析

ip2region xdb 数据结构和查询过程的详解:ip2region xdb 数据结构和查询过程的详解
这篇文章写得很清楚我就不再叙述了。
ps:需要特别提一点的是,该方式查询国内的IP还是比较准确的,基本上能精确到省市。但是国外的一些IP,大体的国家信息没啥问题,再具体的信息可能就不全了。但是一般项目都够用。

查询结果展示:
在这里插入图片描述

查询结果统一采用的是 国家|区域|省份|城市|ISP 的方式返回。

二、在线方式

2.1 接口地址

在线方式网页测试:在线方式网页测试

在这里插入图片描述
只需要通过GET请求该链接 http://ip-api.com/json/ 并后面拼接你的ip,如 http://ip-api.com/json/24.48.0.1 即可。
也能通过添加lang参数让该接口返回中文。如 http://ip-api.com/json/24.48.0.1?lang=zh-CN,就可以返回中文数据了。

2.2 代码实现

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;

@Slf4j
@Component
public class IpUtil {

    private static RestTemplate restTemplate;

    private final RestTemplate template;

    public static final String IP_API_URL = "http://ip-api.com/json/";

    public IpUtil(RestTemplate restTemplate) {
        this.template = restTemplate;
    }

    /**
     * 初始化 RestTemplate
     */
    @PostConstruct
    public void init() {
        setRestTemplate(this.template);
    }

    /**
     * 初始化 RestTemplate
     */
    private static void setRestTemplate(RestTemplate template) {
        restTemplate = template;
    }

    /**
     * 通过ip地址获取所属国家-在线方式
     *
     * @param ip    ip地址
     * @return      国家信息
     */
    public static CountryInfo getCountryByIpOnline(String ip) {
        ResponseEntity<CountryInfo> entity = restTemplate.getForEntity(
                IP_API_URL + ip + "?lang=zh-CN",
                CountryInfo.class
        );
        return entity.getBody();
    }

    /**
     * 在线方式接收类
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class CountryInfo {
        private String query;
        private String status;
        private String country;
        private String countryCode;
        private String region;
        private String regionName;
        private String city;
        private String zip;
        private String lat;
        private String lon;
        private String timezone;
        private String isp;
        private String org;
        private String as;
    }

}

代码也非常简单。

三、使用建议

这里我推荐我们项目中都统一采用先离线后在线方式兜底的写法来处理。因为谁也不能保证离线的就一定能查询到具体的归属地,如果我们使用离线方式查询不到具体的归属地(获取结果为null),那么我建议再使用在线查询的方式来做一个兜底处理,如果都拿不到,再返回空,或者根据自己的业务具体处理。
以上仅作为建议。

四、获取请求IP地址

这里附带一下查询请求的ip地址方法,有需要的可以直接拿走。

/**
 * 获取Ip地址
 *
 * @author dxc
 * @date 2022/7/12 18:18
 */
public final class IpUtil {

    private static final String UNKNOWN = "unknown";

    private IpUtil() {}

    /**
     * 获取客户端ip 地址
     *
     * @param request 请求
     * @return {@link String}
     */
    public static String getClientIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");

        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_CLUSTER_CLIENT_IP");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED_FOR");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_FORWARDED");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_VIA");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("REMOTE_ADDR");
        }
        if (!StringUtils.hasLength(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 多次反向代理后会有多个ip值,第一个ip才是真实ip
        int index = ip.indexOf(",");
        if (index != -1) {
            return ip.substring(0, index);
        } else {
            return ip;
        }
    }
}
  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值