首页 > 应用开发 > Java > 全网都在显示的IP归属地,5分钟带你加上,就这么简单
image-20220515220102985 如遇图片加载失败,可尝试使用手机流量访问
大家好,我是一航!
最近,继新浪微博之后,今日头条、腾讯、抖音、知乎、快手、小红书、百家号等各大平台陆陆续续都上线了" 网络用户IP地址显示功能",境外用户显示的是国家,国内的用户显示的省份,而且此项显示无法关闭,归属地强制显示;
作为技术人,那!这个功能要怎么实现呢?
其实要想实现这个功能还是非常的容易,基于现成 GeoLite2离线库+免费的在线解析资源,5分钟就能整合了;
在整合之前,我们先简单了解一下,要想拿到用户的位置信息,有那些方式:
终端定位
我们的手机等电子设备都是带有GPS定位功能的,APP可以申请权限获取用户所处的经纬度坐标,根据坐标,就可以知道到用户所处的位置;比如百度、高德等地图厂商,就提供了完善的SDK,能非常方便的集成到应用,快速根据经纬度获取详细的位置详细;
优点
缺点
IP地址获取
用户向服务端发起的请求都会带上IP地址,服务端拿到IP地址后,就能基于IP解析出用户的所处的位置;
优点
缺点
三方终端上报
比如,我们骑共享单车的时候,我们的位置信息就是通过单车的设备上报到服务器;
优点
缺点
下面就来试着将 GeoLite2 免费 IP 库整合值SpringBoot项目,来获取用户的归属地信息;
GeoLite2数据库是免费的IP地理定位数据库;
优点:
缺点:
官网地址:https://www.maxmind.com/en/home
下载过程稍微有点点麻烦,这里下载了一份最新的,放在网盘,需要测试的可以直接通过这个链接下载:https://www.123pan.com/s/xPY9-J37vH
工具类
public
class
IpUtils {
/**
* 获取用户IP
*
@param request
*
@return
*/
public
static String
getIpAddr
(HttpServletRequest request) {
String ip = request.getHeader(
"x-forwarded-for");
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader(
"X-Real-IP");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader(
"http_client_ip");
}
if (ip ==
null || ip.length() ==
0 ||
"unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
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.getHeader(
"HTTP_X_FORWARDED_FOR");
}
// 如果是多级代理,那么取第一个ip为客户ip
if (ip !=
null && ip.indexOf(
",") != -
1) {
ip = ip.substring(ip.lastIndexOf(
",") +
1).trim();
}
return ip;
}
}
Controller获取HttpServletRequest
通过上面的工具类,即可获取用户请求的真实IP;
为了避免重复工作,这里也可以使用AOP解析出用户的IP信息,放到用户的请求对象中
@RestController
public
class
IpController {
@GetMapping(
"/user/ip")
public String
userIp
(HttpServletRequest request) {
// 这里就能拿到用户的真实IP
return IpUtils.getIpAddr(request);
}
}
添加依赖
<
dependency>
<
groupId>com.maxmind.geoip2
</
groupId>
<
artifactId>geoip2
</
artifactId>
<
version>2.3.0
</
version>
</
dependency>
<
dependency>
<
groupId>com.maxmind.db
</
groupId>
<
artifactId>maxmind-db
</
artifactId>
<
version>1.0.0
</
version>
</
dependency>
工具类
public
class
GeoIpUtils {
private
static DatabaseReader reader;
private
static
void
init
() {
try {
// 创建 GeoLite2 数据库 Reader
// 这里可以放在本地磁盘,也可以随项目放在resource目录下
File database =
new File(
"F:\\web\\GeoLite2-City.mmdb");
// 读取数据库内容
reader =
new DatabaseReader.Builder(database).build();
}
catch (Exception ex) {
}
}
public
static
void
getCityByIP
(String ip)
throws Exception {
if (
null == reader) {
init();
}
InetAddress ipAddress = InetAddress.getByName(ip);
// 获取查询结果
CityResponse response = reader.city(ipAddress);
// 获取国家信息
Country country = response.getCountry();
System.out.println(
"国家信息:" + JSON.toJSONString(country));
// 获取省份
Subdivision subdivision = response.getMostSpecificSubdivision();
System.out.println(
"省份信息:" + JSON.toJSONString(subdivision));
//城市
City city = response.getCity();
System.out.println(
"城市信息:" + JSON.toJSONString(city));
// 获取城市
Location location = response.getLocation();
System.out.println(
"经纬度信息:" + JSON.toJSONString(location));
}
}
测试
public
static
void
main
(String[] args)
throws Exception {
String ip =
"183.19.xxx.138";
GeoIpUtils.getCityByIP(ip);
}
输出结果:
国家信息:{
"geoNameId":
1814991,
"isoCode":
"CN",
"name":
"China",
"names":{
"de":
"China",
"ru":
"Китай",
"pt-BR":
"China",
"ja":
"中国",
"en":
"China",
"fr":
"Chine",
"zh-CN":
"中国",
"es":
"China"}}
省份信息:{
"geoNameId":
1809935,
"isoCode":
"GD",
"name":
"Guangdong",
"names":{
"en":
"Guangdong",
"fr":
"Province de Guangdong",
"zh-CN":
"广东"}}
城市信息:{
"geoNameId":
1998011,
"name":
"Yanqianlaocun",
"names":{
"en":
"Yanqianlaocun",
"zh-CN":
"岩前老村"}}
经纬度信息:{
"accuracyRadius":
500,
"latitude":
23.3255,
"longitude":
116.5007,
"timeZone":
"Asia/Shanghai"}
就这么简单,轻轻松松就能拿到用户IP所处的国家、省份、城市、经纬度等详细信息,可以根据自己的业务需要,对这些数据再做进一步的封装。
上面介绍的时SpringBoot整合GeoLite2,同样在其他的一些场景下,也是可以利用GeoLite2获取归属地信息;
整合至Nignx,获取用户归属地信息
Nginx 整合 GeoLite2 来解析用户的归属地信息,在代理层就直接整理好对应的数据;
ELK中整合GeoLite2
ELK 日志整理的时候,可以通过GeoLite2 获取用户的IP归属地信息;然后通过Kibana,就能非常直观的展示用户的地域分布情况;
上面一开始介绍GeoLite2时就列举了其离线库更新收录不及时的问题,可能导致一些IP在离线库中并不存在,查找的时候,就会报
AddressNotFoundException的错误,如下示例:
如遇图片加载失败,可尝试使用手机流量访问
遇到这种请求,我们要怎么办呢?
下面就来介绍几种在线IP归属地获取的方式,当本地离线库无法获取的时候,就可以利用三方的在线库,来补充完善;
在线获取的优点:
缺点
以下示例中的xxx.xxx.xxx.xxx均代表ip地址;
地址:https://opendata.baidu.com/api.php?query=xxx.xxx.xxx.xxx&resource_id=6006&co=&oe=utf8
响应数据:
{
"status":
"0",
"t":
"",
"set_cache_time":
"",
"data": [
{
"ExtendedLocation":
"",
"OriginQuery":
"183.19.xxx.138",
"appinfo":
"",
"disp_type":
0,
"fetchkey":
"183.19.xxx.138",
"location":
"广东省肇庆市 电信",
"origip":
"183.19.xxx.138",
"origipquery":
"183.19.xxx.138",
"resourceid":
"6006",
"role_id":
0,
"shareImage":
1,
"showLikeShare":
1,
"showlamp":
"1",
"titlecont":
"IP地址查询",
"tplt":
"ip"
}
]
}
status等于0表示成功,1表示失败;可能存在status等于0,但是data中没有数据的情况。
本机的IP信息
http://ip-api.com/json/
指定国际化
http://ip-api.com/json/?lang=zh-CN
指定IP查询
http://ip-api.com/json/xxx.xxx.xxx.xxx?lang=zh-CN
返回数据:
{
"status":
"success",
"country":
"中国",
"countryCode":
"CN",
"region":
"GD",
"regionName":
"广东",
"city":
"岩前老村",
"zip":
"",
"lat":
23.3255,
"lon":
116.5007,
"timezone":
"Asia/Shanghai",
"isp":
"Chinanet",
"org":
"Chinanet GD",
"as":
"AS4134 CHINANET-BACKBONE",
"query":
"183.19.xxx.138"
}
http://pv.sohu.com/cityjson?ie=utf-8
返回数据比较的简单:
var returnCitySN = {
"cip":
"xxx.xxx.xxx.xxx",
"cid":
"440300",
"cname":
"广东省深圳市"};
地址:http://whois.pconline.com.cn/ipJson.jsp?ip=xxx.xxx.xxx.xxx&json=true
返回数据:
{
"ip":
"183.17.xxx.138",
"pro":
"广东省",
"proCode":
"440000",
"city":
"深圳市",
"cityCode":
"440300",
"region":
"",
"regionCode":
"0",
"addr":
"广东省深圳市 电信",
"regionNames":
"",
"err":
""
}
http://ip.taobao.com/service/getIpInfo.php?ip=xxx.xxx.xxx.xxx
{
"code":
0,
"data": {
"ip":
"183.17.xxx.138",
"country":
"中国",
"area":
"",
"region":
"广东",
"city":
"深圳",
"county":
"XX",
"isp":
"电信"
}
}
code等于0表示成功,1表示失败
地址:https://ip.ws.126.net/ipquery?ip=xxx.xxx.xxx.xxx
响应数据:
var lo=
"广东省", lc=
"肇庆市";
var localAddress={
city:
"肇庆市",
province:
"广东省"}
响应的数据比较的简单
地址:https://ip.useragentinfo.com/json?ip=xxx.xxx.xxx.xxx
响应数据:
{
"country":
"中国",
"short_name":
"CN",
"province":
"广东省",
"city":
"肇庆市",
"area":
"德庆县",
"isp":
"电信",
"net":
"",
"ip":
"183.19.xxx.138",
"code":
200,
"desc":
"success"
}
这么多的姿势,实现起来是不是就非常的容易了;如果你对IP解析的需求比较依赖,也完全可以通过离线加这么多在线的方式,开发一个单独的IP解析模块,作为公司的基础服务,提供给内部其他模块使用。
好了,今天的分享就到这里,感谢你的点赞、关注、收藏!
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70035356/viewspace-2996036/ ,如需转载,请注明出处,否则将追究法律责任。