前言
最近网站有个新需求,获取用户ip所在归属地,于是在网上搜索了好多资料并成功实现,其中遇到了不长坑以及成功解决,现记录下来分享给大家。
准备
获取ip归属地有第三方的也有Java版本的实现,大家可以根据自己实际情况来实现。
1、第三方API查询实现(第三方实现前提是要知道具体的IP)。
2、Java实现(本文就是使用Java来实现的)。
Java实现
我使用的是ip2region来实现的,这里注意一下,ip2region老版本是使用.db库,而新版本使用的是.xdb库,具体区别这里有个文章可以看看。
1、下载.xdb数据库,并保存到/src/resources目录下
2、引入ip2region包依赖
org.lionsoul
ip2region
2.7.0
3、获取ip地址
这里我们使用X-Forwarded-For
来获取客户端的ip,如下所示:
IpUtils.java
public static String getIpAddr(ServerHttpRequest request){
String ipAddress = null;
try {
List xForwardedForHeader = request.getHeaders().get("X-Forwarded-For");
log.info("----------- xForwardedForHeader={}", xForwardedForHeader);
if (xForwardedForHeader != null && !xForwardedForHeader.isEmpty()) {
ipAddress = xForwardedForHeader.get(0);
if(ipAddress.indexOf(",") != -1) {
ipAddress = ipAddress.split(",")[0];
}
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
List proxyClientIPHeader = request.getHeaders().get("Proxy-Client-IP");
ipAddress = (proxyClientIPHeader != null && !proxyClientIPHeader.isEmpty()) ? proxyClientIPHeader.get(0) : null;
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
List wlProxyClientIPHeader = request.getHeaders().get("WL-Proxy-Client-IP");
ipAddress = (wlProxyClientIPHeader != null && !wlProxyClientIPHeader.isEmpty()) ? wlProxyClientIPHeader.get(0) : null;
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
List httpClientIPHeader = request.getHeaders().get("HTTP_CLIENT_IP");
ipAddress = (httpClientIPHeader != null && !httpClientIPHeader.isEmpty()) ? httpClientIPHeader.get(0) : null;
}
if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddress().getAddress().getHostAddress();
}
}catch (Exception e) {
log.error("IPUtils ERROR ",e);
}
return ipAddress;
}
4、解析ip归属地
有了ip,我们使用ip2region
来解析ip归属地,如下所示:
public static byte[] toByteArray(InputStream is) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
// 搜索ip地址
public String searchIpAddr(ServerHttpRequest request){
String ip = IpUtils.getIpAddr(request);
log.info("---------ip={}", ip);
Searcher searcher = null;
try {
InputStream fileStream = new ClassPathResource("ipdb/ip2region.xdb").getInputStream();
searcher = Searcher.newWithBuffer(toByteArray(fileStream));
} catch (IOException e) {
return "";
}
try {
String region = searcher.search(ip);
searcher.close();
return region;
} catch (Exception e) {
}
return "";
}
这段代码需要注意两点:
1、一定要使用ClassPathResource("ipdb/ip2region.xdb").getInputStream()
获取文件位置,否则获取不到文件。
2、将文件流转换成byte[],然后使用Searcher.newWithBuffer
来解析ip归属地。
完成之后,我们通过k8s发布到生产试试看看可不可以拿到ip归属地region
,如下所示:
IP属地:中国|0|江苏省|南京市|电信
总结
1、注意本地的ip是127.0.0.1。
2、接下来我再写一篇文章关于next.js遇到的获取不到真实Ip问题。