Java实现通过IP计算分析归属地实例分享

 更新时间:2025年05月01日 09:24:16   作者:七号楼  
文章介绍了如何通过IP地址进行归属地分析,包括IP地址的两种类型(IPV4和IPV6)以及计算归属地的方法,对于不精准的需求,推荐使用开源的字典库如GeoIP2;对于高精度需求,可以购买专业的IP网段数据并实时更新数据库,在设计项目时应提前规划数据结构,以避免数据清洗问题

在产品中可能存在不同客户端,请求同一个服务端接口的场景。

例如小程序和App或者浏览器中,如果需要对请求的归属地进行分析,前提是需要先获取请求所在的国家或城市,这种定位通常需要主动授权,而用户一般是不愿意提供的,就需要通过请求的IP来进行归属地计算。

IP地址一般分为两种,IPV4和IPV6,相应的计算方式也有差异,以国家维度来参考,每个国家都有对应的网段范围,计算网段中的最小和最大IP地址的对应数值,然后对比请求的IP地址,来判断属于哪个国家的网段范围。

import cn.hutool.core.net.Ipv4Util;
import cn.hutool.core.util.StrUtil;
import java.math.BigInteger;
import java.net.InetAddress;

public class IpCalculate {
    public static void main(String[] args) throws Exception {
        // IPv4 网段
        String ipv4Network = "IPv4 网段";
        String[] ipv4Param = StrUtil.splitToArray(ipv4Network, "/");

        // IPv4 起始和结束IP
        String ipv4StartIp = Ipv4Util.getBeginIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1]));
        String ipv4OverIp = Ipv4Util.getEndIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1]));
        System.out.println(ipv4StartIp);
        System.out.println(ipv4OverIp);

        // IPv4 起始和结束IP对应的Long值
        System.out.println(Ipv4Util.ipv4ToLong(ipv4StartIp));
        System.out.println(Ipv4Util.ipv4ToLong(ipv4OverIp));

        // IPv6 网段
        String ipv6Network = "IPv6 网段";
        String[] ipv6Param =ipv6Network.split("/");
        int prefixLength = Integer.parseInt(ipv6Param[1]);

        // IPv6 起始和结束IP
        InetAddress baseAddress = InetAddress.getByName(ipv6Param[0]);
        BigInteger baseValue = new BigInteger(1, baseAddress.getAddress());
        BigInteger mask = BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE)
                .shiftRight(128 - prefixLength).shiftLeft(128 - prefixLength);
        BigInteger minIp = baseValue.and(mask);
        BigInteger maxIp = minIp.add(BigInteger.ONE.shiftLeft(128 - prefixLength).subtract(BigInteger.ONE));
        System.out.println(toIPv6String(minIp));
        System.out.println(toIPv6String(maxIp));

        // IPv6 起始和结束IP对应的Long值
        System.out.println(minIp);
        System.out.println(maxIp);
    }

    private static String toIPv6String(BigInteger value) throws Exception {
        byte[] bytes = value.toByteArray();
        byte[] ipv6Bytes = new byte[16];
        int start = bytes.length > 16 ? bytes.length - 16 : 0;
        int length = Math.min(bytes.length, 16);
        System.arraycopy(bytes, start, ipv6Bytes, 16 - length, length);
        return InetAddress.getByAddress(ipv6Bytes).getHostAddress();
    }
}

不过网段地址和国家的对应关系需要进行维护,如果归属地分析不需要非常精准,可以直接使用开源的字典库,比如使用比较多的就是GeoIP2组件。

<dependency>
  <groupId>com.maxmind.geoip2groupId>
  <artifactId>geoip2</artifactId>
</dependency>

通过组件中提供的API加载相应的文件字典,然后传入IP地址进行归属地判断,这里要注意争议和敏感地区的处理,如果出错产品可不止是上热搜的问题了。

import com.maxmind.geoip2.DatabaseReader;
import java.io.File;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;

public class GeoIpTool {
    public static void main(String[] args) throws Exception {

        // 读取IP库文件
        File ipFile = new File("IP文件库");
        DatabaseReader reader = new DatabaseReader.Builder(ipFile).build();

        // IPV4地址
        InetAddress ipV4 = InetAddress.getByName("IPV4");
        if (ipV4 instanceof Inet4Address){
            System.out.println(reader.country(ipV4));
            System.out.println(reader.country(ipV4).getCountry());
            // 默认英文名
            System.out.println(reader.country(ipV4).getCountry().getName());
            // 查询中文名
            System.out.println(reader.country(ipV4).getCountry().getNames().get("zh-CN"));
        }

        // IPV6地址
        InetAddress ipV6 = InetAddress.getByName("IPV6");
        if (ipV6 instanceof Inet6Address){
            System.out.println(reader.country(ipV6));
            System.out.println(reader.country(ipV6).getCountry());
            // 默认英文名
            System.out.println(reader.country(ipV6).getCountry().getName());
            // 查询中文名
            System.out.println(reader.country(ipV6).getCountry().getNames().get("zh-CN"));
        }
    }
}

如果需要非常精确的实时归属地分析,可以购买专业的IP网段数据,实时更新到本地的数据库中,作为IP字典使用,获取请求的IP后,直接范围匹配即可。

CREATE TABLE `ip_place` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `network` varchar(100) DEFAULT NULL COMMENT '网段区间',
  `min_ip` bigint(20) DEFAULT NULL COMMENT '最小IP',
  `max_ip` bigint(20) DEFAULT NULL COMMENT '最大IP',
  `min_ip_number` bigint(20) DEFAULT NULL COMMENT '最小IP数值',
  `max_ip_number` bigint(20) DEFAULT NULL COMMENT '最大IP数值',
  `ip_place` varchar(100) DEFAULT NULL COMMENT '归属地',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP归属地';

最后需要补充说一句,对于很多标准的数据,尽可能在项目最初就设计好字典枚举或者数据表,避免后续规范时面临数据清洗的问题。

到此这篇关于Java实现通过IP计算分析归属地实例分享的文章就介绍到这了,更多相关通过IP计算分析归属地内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java NIO实例UDP发送接收数据代码分享

    Java NIO实例UDP发送接收数据代码分享

    这篇文章主要介绍了Java NIO实例UDP发送接收数据代码分享,分享了客户端和服务端完整代码,小编觉得还是挺不错的,共需要的朋友参考。
    2017-11-11
  • springboot使用redis实现从配置到实战

    springboot使用redis实现从配置到实战

    本文主要介绍了springboot使用redis ,采用的是RedisTemplate的形式,还有一种采用spring支持的注解进行访问缓存,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • SpringBoot获取真实客户端IP的正确方法

    SpringBoot获取真实客户端IP的正确方法

    在日常开发中,获取客户端IP看似简单,实则暗藏玄机,很多开发者直接使用request.getRemoteAddr(),结果在生产环境中发现获取到的都是负载均衡器的IP,而非真实用户IP,本文将彻底揭秘Spring Boot中获取真实客户端IP的正确姿势,需要的朋友可以参考下
    2026-03-03
  • 教你如何用Java替换Word中带有${}的内容

    教你如何用Java替换Word中带有${}的内容

    这篇文章主要介绍了教你如何用Java替换Word中带有${}的内容,文中有非常详细的代码示例,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下
    2021-04-04
  • 解决SpringCloud Gateway配置自定义路由404的坑

    解决SpringCloud Gateway配置自定义路由404的坑

    这篇文章主要介绍了解决SpringCloud Gateway配置自定义路由404的坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • 详解Java 中的 AutoCloseable 接口

    详解Java 中的 AutoCloseable 接口

    本文对 try-with-resources 语法进行了较为深入的剖析,验证了其为一种语法糖,同时给出了其实际的实现方式的反编译结果,相信你在看完本文后,关于 AutoCloseable 的使用你会有新的收获。
    2020-11-11
  • Fluent Mybatis,原生Mybatis,Mybatis Plus三者功能对比

    Fluent Mybatis,原生Mybatis,Mybatis Plus三者功能对比

    本文主要介绍了Fluent Mybatis,原生Mybatis,Mybatis Plus三者功能对比,分享给大家,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • mybatis Invalid bound statement(not found)排坑记录

    mybatis Invalid bound statement(not foun

    这篇文章主要介绍了mybatis Invalid bound statement(not found)排坑记录,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • SpringBoot超详细深入讲解底层原理

    SpringBoot超详细深入讲解底层原理

    我们知道springboot内部是通过spring框架内嵌Tomcat实现的,当然也可以内嵌jetty,undertow等等web框架;另外springboot还有一个特别重要的功能就是自动装配,这又是如何实现的呢
    2022-07-07
  • 简洁实用的Java Base64编码加密异常处理类代码

    简洁实用的Java Base64编码加密异常处理类代码

    这篇文章主要介绍了简洁实用的Java Base64编码加密异常处理类代码,有一定的实用价值,需要的朋友可以参考下
    2014-07-07

最新评论