当前位置: 首页 > news >正文

通过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 起始和结束IPString 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 起始和结束IPInetAddress 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归属地';

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

http://www.xdnf.cn/news/223147.html

相关文章:

  • 知识图谱系列(1):基础概念与发展历程
  • ubuntu22.04出现VFS: Unable to mount root fs on unknown-block(0,0)
  • 网络规划和设计
  • ceph存储原理
  • 人格伤疤测试:发现内心深处的情感创伤
  • 【今日三题】kotori和气球(排列) / 走迷宫(BFS最短路) / 主持人调度(二)(贪心+优先级队列)
  • 服务端字符过滤 与 SQL PDO防注入
  • [C语言]猜数字游戏
  • 软件系统容量管理:反模式剖析与模式应用
  • 什么是环境变量,main函数的命令行参数的概念和作用,以及进程地址空间详解
  • antd中的表格穿梭框(Transfer)如何使用
  • 类和对象 (拷贝构造函数和运算符重载)上
  • MySQL学习总结
  • 华锐视点历经十八年沉淀所形成的产品特性
  • 【AI平台】n8n入门4:n8n云创建工作流(无须搭建,快速试用14天)
  • TypeScript 全局类型声明文件规范性分析与归纳
  • 赛事季突围!备战2025全国信息素养大赛 python挑战赛~
  • JavaScript 相关知识点整理
  • 【LLM】Qwen3模型训练和推理
  • Proser:重新介绍
  • Linux(权限管理)
  • FastAPI的发展历史
  • 在VMware上创建Ubuntu虚拟机,与Xshell和Xftp的连接和使用
  • 文心一言开发指南08——千帆大模型平台推理服务API
  • 9.idea中创建springboot项目_jdk1.8
  • 无需下载,免费在线AI音乐生成器:爱狐AI详解与使用
  • C# 继承详解
  • LangChain4j +DeepSeek大模型应用开发——5 持久化聊天记忆 Persistence
  • 问题整理篇---(1)keil
  • Linux查看程序端口占用情况