通过IP计算分析归属地

打印 上一主题 下一主题

主题 1922|帖子 1922|积分 5770

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在产品中可能存在不同客户端,哀求同一个服务端接口的场景。
例如小程序和App或者浏览器中,假如需要对哀求的归属地进行分析,前提是需要先获取哀求所在的国家或城市,这种定位通常需要主动授权,而用户一样平常是不愿意提供的,就需要通过哀求的IP来进行归属地计算。
IP地址一样平常分为两种,IPV4和IPV6,相应的计算方式也有差异,以国家维度来参考,每个国家都有对应的网段范围,计算网段中的最小和最大IP地址的对应数值,然后对比哀求的IP地址,来判断属于哪个国家的网段范围。
  1. import cn.hutool.core.net.Ipv4Util;
  2. import cn.hutool.core.util.StrUtil;
  3. import java.math.BigInteger;
  4. import java.net.InetAddress;
  5. public class IpCalculate {
  6.     public static void main(String[] args) throws Exception {
  7.         // IPv4 网段
  8.         String ipv4Network = "IPv4 网段";
  9.         String[] ipv4Param = StrUtil.splitToArray(ipv4Network, "/");
  10.         // IPv4 起始和结束IP
  11.         String ipv4StartIp = Ipv4Util.getBeginIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1]));
  12.         String ipv4OverIp = Ipv4Util.getEndIpStr(ipv4Param[0],Integer.parseInt(ipv4Param[1]));
  13.         System.out.println(ipv4StartIp);
  14.         System.out.println(ipv4OverIp);
  15.         // IPv4 起始和结束IP对应的Long值
  16.         System.out.println(Ipv4Util.ipv4ToLong(ipv4StartIp));
  17.         System.out.println(Ipv4Util.ipv4ToLong(ipv4OverIp));
  18.         // IPv6 网段
  19.         String ipv6Network = "IPv6 网段";
  20.         String[] ipv6Param =ipv6Network.split("/");
  21.         int prefixLength = Integer.parseInt(ipv6Param[1]);
  22.         // IPv6 起始和结束IP
  23.         InetAddress baseAddress = InetAddress.getByName(ipv6Param[0]);
  24.         BigInteger baseValue = new BigInteger(1, baseAddress.getAddress());
  25.         BigInteger mask = BigInteger.ONE.shiftLeft(128).subtract(BigInteger.ONE)
  26.                 .shiftRight(128 - prefixLength).shiftLeft(128 - prefixLength);
  27.         BigInteger minIp = baseValue.and(mask);
  28.         BigInteger maxIp = minIp.add(BigInteger.ONE.shiftLeft(128 - prefixLength).subtract(BigInteger.ONE));
  29.         System.out.println(toIPv6String(minIp));
  30.         System.out.println(toIPv6String(maxIp));
  31.         // IPv6 起始和结束IP对应的Long值
  32.         System.out.println(minIp);
  33.         System.out.println(maxIp);
  34.     }
  35.     private static String toIPv6String(BigInteger value) throws Exception {
  36.         byte[] bytes = value.toByteArray();
  37.         byte[] ipv6Bytes = new byte[16];
  38.         int start = bytes.length > 16 ? bytes.length - 16 : 0;
  39.         int length = Math.min(bytes.length, 16);
  40.         System.arraycopy(bytes, start, ipv6Bytes, 16 - length, length);
  41.         return InetAddress.getByAddress(ipv6Bytes).getHostAddress();
  42.     }
  43. }
复制代码
不过网段地址和国家的对应关系需要进行维护,假如归属地分析不需要非常精准,可以直接使用开源的字典库,比如使用比较多的就是GeoIP2组件。
  1. <dependency>
  2.   <groupId>com.maxmind.geoip2groupId>
  3.   <artifactId>geoip2</artifactId>
  4. </dependency>
复制代码
通过组件中提供的API加载相应的文件字典,然后传入IP地址进行归属地判断,这里要注意争媾和敏感地区的处理,假如出错产品可不止是上热搜的问题了。
  1. import com.maxmind.geoip2.DatabaseReader;
  2. import java.io.File;
  3. import java.net.Inet4Address;
  4. import java.net.Inet6Address;
  5. import java.net.InetAddress;
  6. public class GeoIpTool {
  7.     public static void main(String[] args) throws Exception {
  8.         // 读取IP库文件
  9.         File ipFile = new File("IP文件库");
  10.         DatabaseReader reader = new DatabaseReader.Builder(ipFile).build();
  11.         // IPV4地址
  12.         InetAddress ipV4 = InetAddress.getByName("IPV4");
  13.         if (ipV4 instanceof Inet4Address){
  14.             System.out.println(reader.country(ipV4));
  15.             System.out.println(reader.country(ipV4).getCountry());
  16.             // 默认英文名
  17.             System.out.println(reader.country(ipV4).getCountry().getName());
  18.             // 查询中文名
  19.             System.out.println(reader.country(ipV4).getCountry().getNames().get("zh-CN"));
  20.         }
  21.         // IPV6地址
  22.         InetAddress ipV6 = InetAddress.getByName("IPV6");
  23.         if (ipV6 instanceof Inet6Address){
  24.             System.out.println(reader.country(ipV6));
  25.             System.out.println(reader.country(ipV6).getCountry());
  26.             // 默认英文名
  27.             System.out.println(reader.country(ipV6).getCountry().getName());
  28.             // 查询中文名
  29.             System.out.println(reader.country(ipV6).getCountry().getNames().get("zh-CN"));
  30.         }
  31.     }
  32. }
复制代码
假如需要非常精确的实时归属地分析,可以购买专业的IP网段数据,实时更新到本地的数据库中,作为IP字典使用,获取哀求的IP后,直接范围匹配即可。
  1. CREATE TABLE `ip_place` (
  2.   `id` bigint(20) NOT NULL AUTO_INCREMENT,
  3.   `network` varchar(100) DEFAULT NULL COMMENT '网段区间',
  4.   `min_ip` bigint(20) DEFAULT NULL COMMENT '最小IP',
  5.   `max_ip` bigint(20) DEFAULT NULL COMMENT '最大IP',
  6.   `min_ip_number` bigint(20) DEFAULT NULL COMMENT '最小IP数值',
  7.   `max_ip_number` bigint(20) DEFAULT NULL COMMENT '最大IP数值',
  8.   `ip_place` varchar(100) DEFAULT NULL COMMENT '归属地',
  9.   PRIMARY KEY (`id`)
  10. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP归属地';
复制代码
末了需要增补说一句,对于很多尺度的数据,尽可能在项目最初就设计好字典摆列或者数据表,避免后续规范时面临数据清洗的问题。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

刘俊凯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表