使用 Java 获取 IP 归属地的方法与实现

本贴最后更新于 612 天前,其中的信息可能已经斗转星移

引言

在网络应用开发中,我们常常需要获取 IP 地址的归属地信息,这对于数据统计、用户分析和风险控制等方面都非常重要。本文将介绍如何使用 Java 编程语言获取 IP 地址的归属地,并提供一个实际的案例。

什么是 IP 归属地

“IP 属地”指 IP 地址所在省(自治区、直辖市)(针对境内账号)或国家(地区)(针对境外账号),比如上海、北京、江苏;美国、日本等。

使用 Java 获取 IP 归属地的方法

我们可以通过调用第三方的 IP 归属地查询接口,或者使用 Java 开发者自行解析 IP 数据库的方式来获取 IP 归属地信息。

方法一:本地数据库实现:ip2region

1.1、首先下载 IP 数据库文件:

戳 >>>>>>>>>>>>>>>数据库文件

1.2、Java 中获取请求 IP 的方法

private static final String UNKNOWN = "unknown"; public String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ip.contains(",")) { ip = ip.split(",")[0]; } return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; }

2、maven 仓库:

<!-- 离线IP归属地查询 --> <dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>2.7.0</version> </dependency>

3.1、完全基于文件的查询

代码中 dbPath 换成前面下载数据文件绝对路径

import org.lionsoul.ip2region.xdb.Searcher; import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { // 1、创建 searcher 对象 String dbPath = "ip2region.xdb file path"; Searcher searcher = null; try { searcher = Searcher.newWithFileOnly(dbPath); } catch (IOException e) { System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e); return; } // 2、查询 try { String ip = "1.2.3.4"; long sTime = System.nanoTime(); String region = searcher.search(ip); long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); } catch (Exception e) { System.out.printf("failed to search(%s): %s\n", ip, e); } // 3、关闭资源 searcher.close(); // 备注:并发使用,每个线程需要创建一个独立的 searcher 对象单独使用。 } }

3.2、 缓存 VectorIndex 索引

我们可以提前从 xdb 文件中加载出来 VectorIndex 数据,然后全局缓存,每次创建 Searcher 对象的时候使用全局的 VectorIndex 缓存可以减少一次固定的 IO 操作,从而加速查询,减少 IO 压力。

import org.lionsoul.ip2region.xdb.Searcher; import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { String dbPath = "ip2region.xdb file path"; // 1、从 dbPath 中预先加载 VectorIndex 缓存,并且把这个得到的数据作为全局变量,后续反复使用。 byte[] vIndex; try { vIndex = Searcher.loadVectorIndexFromFile(dbPath); } catch (Exception e) { System.out.printf("failed to load vector index from `%s`: %s\n", dbPath, e); return; } // 2、使用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。 Searcher searcher; try { searcher = Searcher.newWithVectorIndex(dbPath, vIndex); } catch (Exception e) { System.out.printf("failed to create vectorIndex cached searcher with `%s`: %s\n", dbPath, e); return; } // 3、查询 try { String ip = "1.2.3.4"; long sTime = System.nanoTime(); String region = searcher.search(ip); long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); } catch (Exception e) { System.out.printf("failed to search(%s): %s\n", ip, e); } // 4、关闭资源 searcher.close(); // 备注:每个线程需要单独创建一个独立的 Searcher 对象,但是都共享全局的制度 vIndex 缓存。 } }

3.3、缓存整个 xdb 数据

我们也可以预先加载整个 ip2region.xdb 的数据到内存,然后基于这个数据创建查询对象来实现完全基于文件的查询,类似之前的 memory search。

import org.lionsoul.ip2region.xdb.Searcher; import java.io.*; import java.util.concurrent.TimeUnit; public class SearcherTest { public static void main(String[] args) { String dbPath = "ip2region.xdb file path"; // 1、从 dbPath 加载整个 xdb 到内存。 byte[] cBuff; try { cBuff = Searcher.loadContentFromFile(dbPath); } catch (Exception e) { System.out.printf("failed to load content from `%s`: %s\n", dbPath, e); return; } // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。 Searcher searcher; try { searcher = Searcher.newWithBuffer(cBuff); } catch (Exception e) { System.out.printf("failed to create content cached searcher: %s\n", e); return; } // 3、查询 try { String ip = "1.2.3.4"; long sTime = System.nanoTime(); String region = searcher.search(ip); long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime)); System.out.printf("{region: %s, ioCount: %d, took: %d μs}\n", region, searcher.getIOCount(), cost); } catch (Exception e) { System.out.printf("failed to search(%s): %s\n", ip, e); } // 4、关闭资源 - 该 searcher 对象可以安全用于并发,等整个服务关闭的时候再关闭 searcher // searcher.close(); // 备注:并发使用,用整个 xdb 数据缓存创建的查询对象可以安全的用于并发,也就是你可以把这个 searcher 对象做成全局对象去跨线程访问。 } }

方法二:使用第三方接口

现在有很多第三方提供了 IP 归属地查询的接口,我们可以通过发送 HTTP 请求来获取 IP 的归属地信息:

接口一:查询 IP 归属地(自动区分 v4&v6)

https://api.vore.top/api/IPdata?ip=[IP]

测试代码:

//发送请求使用了hutool依赖 public static void main(String[] args) { String url = "https://api.vore.top/api/IPdata?ip={0}"; String formatUrl = MessageFormat.format(url, "137.184.160.151"); String result = HttpUtil.get(formatUrl); System.out.println(result); }

测试打印结果

{ "code": 200, "msg": "SUCCESS", "ipinfo": { "type": "ipv4", "text": "137.184.160.151", "cnip": false }, "ipdata": { "info1": "加拿大", "info2": "安大略省", "info3": "多伦多", "isp": "DigitalOcean有限责任公司" }, "adcode": { "o": "加拿大安大略省多伦多 - DigitalOcean有限责任公司", "p": "加拿大", "c": "安大略省", "n": "加拿大-安大略省", "r": null, "a": null, "i": false }, "tips": "接口由VORE-API(https:\/\/api.vore.top\/)免费提供", "time": 1694417659 } Process finished with exit code 0

接口二:百度 api

http://opendata.baidu.com/api.php?query=117.136.12.79&co=&resource_id=6006&oe=utf8

方法三:在线查询

如果只有少量需求可在线查询,→ 在线 IP 归属地

总结

本文介绍了使用 Java 获取 IP 归属地的方法与实现,包括调用第三方接口和解析 IP 库两种常用方式。通过获取 IP 归属地信息,我们可以进行地理位置分析和业务逻辑处理,从而更好地优化网站和应用。

希望本文对你有所帮助!

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3201 引用 • 8216 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...