在网络应用开发中,我们常常需要获取IP地址的归属地信息,这对于数据统计、用户分析和风险控制等方面都非常重要。本文将介绍如何使用Java编程语言获取IP地址的归属地,并提供一个实际的案例。
“IP属地”指IP地址所在省(自治区、直辖市)(针对境内账号)或国家(地区)(针对境外账号),比如上海、北京、江苏;美国、日本等。
我们可以通过调用第三方的IP归属地查询接口,或者使用Java开发者自行解析IP数据库的方式来获取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;
}
<!-- 离线IP归属地查询 -->
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.7.0</version>
</dependency>
代码中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 对象单独使用。
}
}
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 缓存。
}
}
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的归属地信息:
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
http://opendata.baidu.com/api.php?query=117.136.12.79&co=&resource_id=6006&oe=utf8
如果只有少量需求可在线查询,→在线工具→在线IP归属地
本文介绍了使用Java获取IP归属地的方法与实现,包括调用第三方接口和解析IP库两种常用方式。通过获取IP归属地信息,我们可以进行地理位置分析和业务逻辑处理,从而更好地优化网站和应用。
希望本文对你有所帮助!