• Java根据ip地址获取归属地


    最近,各大平台都新增了评论区显示发言者ip归属地的功能,例如哔哩哔哩,微博,知乎等等。

    下面,我就来讲讲,Java 中是如何获取 IP 属地的,主要分为以下几步

    • 通过 HttpServletRequest 对象,获取用户的 IP 地址
    • 通过 IP 地址,获取对应的省份、城市

    首先需要写一个 IP 获取的工具类,因为每一次用户的 Request 请求,都会携带上请求的 IP 地址放到请求头中。

    1. public class IpUtils {
    2. /**
    3. * 获取ip地址
    4. * @param request
    5. * @return
    6. */
    7. public static String getIpAddr(HttpServletRequest request){
    8. String ipAddress = null;
    9. try {
    10. ipAddress = request.getHeader("X-Forwarded-For");
    11. if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
    12. // 多次反向代理后会有多个ip值,第一个ip才是真实ip
    13. if (ipAddress.indexOf(",") != -1) {
    14. ipAddress = ipAddress.split(",")[0];
    15. }
    16. }
    17. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    18. ipAddress = request.getHeader("Proxy-Client-IP");
    19. }
    20. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    21. ipAddress = request.getHeader("WL-Proxy-Client-IP");
    22. }
    23. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    24. ipAddress = request.getHeader("HTTP_CLIENT_IP");
    25. }
    26. if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
    27. ipAddress = request.getRemoteAddr();
    28. }
    29. }catch (Exception e) {
    30. log.error("IPUtils ERROR ",e);
    31. }
    32. return ipAddress;
    33. }
    34. 复制代码

    对这里出现的几个名词解释一下:

    • 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依赖

    1. <dependency>
    2. <groupId>org.lionsoul</groupId>
    3. <artifactId>ip2region</artifactId>
    4. <version>1.7.2</version>
    5. </dependency>
    6. 复制代码

    2、下载仓库中的ip2region.db 文件,放到工程resources目录下

    3、编写方法加载ip2region文件,对用户ip地址进行转换。

    1. /**
    2. * 获取ip属地
    3. * @param ip
    4. * @return
    5. * @throws Exception
    6. */
    7. public static String getCityInfo(String ip) throws Exception {
    8. //获得文件流时,因为读取的文件是在打好jar文件里面,不能直接通过文件资源路径拿到文件,但是可以在jar包中拿到文件流
    9. ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    10. Resource[] resources = resolver.getResources("ip2region.db");
    11. Resource resource = resources[0];
    12. InputStream is = resource.getInputStream();
    13. File target = new File("ip2region.db");
    14. FileUtils.copyInputStreamToFile(is, target);
    15. is.close();
    16. if (StringUtils.isEmpty(String.valueOf(target))) {
    17. log.error("Error: Invalid ip2region.db file");
    18. return null;
    19. }
    20. DbConfig config = new DbConfig();
    21. DbSearcher searcher = new DbSearcher(config, String.valueOf(target));
    22. //查询算法
    23. //B-tree, B树搜索(更快)
    24. int algorithm = DbSearcher.BTREE_ALGORITHM;
    25. try {
    26. //define the method
    27. Method method;
    28. method = searcher.getClass().getMethod("btreeSearch", String.class);
    29. DataBlock dataBlock;
    30. if (!Util.isIpAddress(ip)) {
    31. log.error("Error: Invalid ip address");
    32. }
    33. dataBlock = (DataBlock) method.invoke(searcher, ip);
    34. String ipInfo = dataBlock.getRegion();
    35. if (!StringUtils.isEmpty(ipInfo)) {
    36. ipInfo = ipInfo.replace("|0", "");
    37. ipInfo = ipInfo.replace("0|", "");
    38. }
    39. return ipInfo;
    40. } catch (Exception e) {
    41. e.printStackTrace();
    42. }
    43. return null;
    44. }
    45. 复制代码

    4、由于 ip 属地在国内的话,只会展示省份,而国外的话,只会展示国家。所以我们还需要对这个方法进行一下封装,得到获取 IP 属地的信息。

    1. public static String getIpPossession(String ip) throws Exception {
    2. String cityInfo = IpUtils.getCityInfo(ip);
    3. if (!StringUtils.isEmpty(cityInfo)) {
    4. cityInfo = cityInfo.replace("|", " ");
    5. String[] cityList = cityInfo.split(" ");
    6. if (cityList.length > 0) {
    7. // 国内的显示到具体的省
    8. if ("中国".equals(cityList[0])) {
    9. if (cityList.length > 1) {
    10. return cityList[1];
    11. }
    12. }
    13. // 国外显示到国家
    14. return cityList[0];
    15. }
    16. }
    17. return "未知";
    18. }
    19. 复制代码

    5、编写测试类。

    1. public static void main(String[] args) throws Exception {
    2. //国内ip
    3. String ip1 = "220.248.12.158";
    4. String cityInfo1 = IpUtils.getCityInfo(ip1);
    5. System.out.println(cityInfo1);
    6. String address1 = IpUtils.getIpPossession(ip1);
    7. System.out.println(address1);
    8. //国外ip
    9. String ip2 = "67.220.90.13";
    10. String cityInfo2 = IpUtils.getCityInfo(ip2);
    11. System.out.println(cityInfo2);
    12. String address2 = IpUtils.getIpPossession(ip2);
    13. System.out.println(address2);
    14. }
    15. 复制代码

    6、测试结果

    最后,附上项目用到的全部依赖,想了解的小伙伴可以学习一下!

    1. <dependency>
    2. <groupId>org.slf4j</groupId>
    3. <artifactId>slf4j-api</artifactId>
    4. <version>1.7.36</version>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.projectlombok</groupId>
    8. <artifactId>lombok</artifactId>
    9. <version>1.18.10</version>
    10. </dependency>
    11. <dependency>
    12. <groupId>org.lionsoul</groupId>
    13. <artifactId>ip2region</artifactId>
    14. <version>1.7.2</version>
    15. </dependency>
    16. <dependency>
    17. <groupId>javax.servlet</groupId>
    18. <artifactId>servlet-api</artifactId>
    19. <version>2.5</version>
    20. <scope>provided</scope>
    21. </dependency>
    22. <dependency>
    23. <groupId>org.apache.commons</groupId>
    24. <artifactId>commons-lang3</artifactId>
    25. <version>3.12.0</version>
    26. </dependency>
    27. <dependency>
    28. <groupId>commons-io</groupId>
    29. <artifactId>commons-io</artifactId>
    30. <version>2.11.0</version>
    31. </dependency>

  • 相关阅读:
    Win11系统重装用什么好 一键重装Win11教程
    深刻理解JAVA并发中的有序性问题和解决之道
    【毕业设计】基于ZigBee的智能灯控系统 -物联网 单片机 stm32
    Springboot JSON 转换:Jackson篇
    git-secret:在 Git 存储库中加密和存储密钥(下)
    python的正则表达中的re库一些常用的方法
    ServletConfig接口干什么
    ECA-Net:深度卷积神经网络的高效通道注意力
    【面试必刷TOP101】链表相加 & 单链表的排序
    数据可视化之智能推荐,智能分析数据,自动推荐可视化图表
  • 原文地址:https://blog.csdn.net/m0_73311735/article/details/126602626