最近微博等平台都上线  IP 属地功能。

下面,我就来讲讲,Java 中是如何获取 IP 属地的,主要分为以下几步

  • 通过 HttpServletRequest 对象,获取用户的 IP 地址

  • 通过 IP 地址,获取对应的省份、城市

首先需要写一个 IP 获取的工具类,因为每一次用户的 Request 请求,都会携带上请求的 IP 地址放到请求头中。

public class IpUtil {public static String getIpAddr(ServerHttpRequest request) {HttpHeaders headers = request.getHeaders();String ipAddress = headers.getFirst("X-Forwarded-For");if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = headers.getFirst("Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = headers.getFirst("WL-Proxy-Client-IP");}if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddress().getAddress().getHostAddress();if (ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")) {// 根据网卡取本机配置的IPtry {InetAddress inet = InetAddress.getLocalHost();ipAddress = inet.getHostAddress();} catch (UnknownHostException e) {log.error("根据网卡获取本机配置的IP异常", e);}}}// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if (ipAddress != null && ipAddress.indexOf(",") > 0) {ipAddress = ipAddress.split(",")[0];}return ipAddress;}
}

这里有三个名词,分别是

  • X-Forwarded-For一个 HTTP 扩展头部,主要是为了让 Web 服务器获取访问用户的真实 IP 地址。每个 IP 地址,每个值通过逗号+空格分开,最左边是最原始客户端的 IP 地址,中间如果有多层代理,每⼀层代理会将连接它的客户端 IP 追加在 X-Forwarded-For 右边。

  • X-Real-IP:一般只记录真实发出请求的客户端IP

  • Proxy-Client-IP:这个一般是经过 Apache http 服务器的请求才会有,用 Apache http 做代理时一般会加上 Proxy-Client-IP 请求头

  • WL-Proxy-Client-IP:也是通过 Apache http 服务器,在 weblogic 插件加上的头。

在我们获取到用户的 IP 地址后,那么就可以获取对应的 ip 信息了

我们蘑菇社区最开始使用的是淘宝 IP 库

地址:https://ip.taobao.com/

淘宝IP地址库

接入方式也比较简单,就是通过封装一个 http 请求,传入用户的 ip 作为参数,就可以返回 ip 对应的国家,省,城市 信息

API接口

原来的请求方式如下

    /*** 获取IP地址来源** @param content        请求的参数 格式为:name=xxx&pwd=xxx* @param encodingString 服务器端请求编码。如GBK,UTF-8等* @return* @throws UnsupportedEncodingException*/public static String getAddresses(String content, String encodingString) {String ip = content.substring(3);if (!Util.isIpAddress(ip)) {log.info("IP地址为空");return null;}// 淘宝IP宕机,目前使用Ip2region:https://github.com/lionsoul2014/ip2regionString cityInfo = getCityInfo(ip);log.info("返回的IP信息:{}", cityInfo);// TODO 淘宝接口目前已经宕机,因此暂时注释下面代码try {// 这里调用pconline的接口String urlStr = "http://ip.taobao.com/service/getIpInfo.php";// 从http://whois.pconline.com.cn取得IP所在的省市区信息String returnStr = getResult(urlStr, content, encodingString);if (returnStr != null) {// 处理返回的省市区信息log.info("调用IP解析接口返回的内容:" + returnStr);String[] temp = returnStr.split(",");//无效IP,局域网测试if (temp.length < 3) {return "0";}// 国家String country = "";// 区域String area = "";// 省String region = "";// 市String city = "";// 县String county = "";// 运营商String isp = "";Map<String, Object> map = JsonUtils.jsonToMap(returnStr);if (map.get("code") != null) {Map<String, String> data = (Map<String, String>) map.get("data");country = data.get("country");area = data.get("area");region = data.get("region");city = data.get("city");county = data.get("area");isp = data.get("isp");}log.info("获取IP地址对应的地址" + country + "=" + area + "=" + region + "=" + city + "=" + county + "=" + isp);StringBuffer result = new StringBuffer();result.append(country);result.append("|");result.append(region);result.append("|");result.append(city);result.append("|");result.append(isp);return result.toString();}} catch (Exception e) {log.error(e.getMessage());return null;}return null;}

但是,之前接入淘宝 IP 库的时候,也经常会遇到服务不可用的情况,并且由于限制了 QPS 为 1,所以如果访问量大的话,就没办法获取了。

而到现在的话倒好了,这个接口也不对外提供服务了,直接下线了,不让调用了。

后面,陌溪在 Github 冲浪的时候,发现了 Ip2region 项目。

一个准确率 99.9% 的离线 IP 地址定位库,0.0x 毫秒级查询,ip2region.db 数据库只有数 MB,提供了 java,php,c,python,nodejs,golang,c# 等查询绑定和BinaryB树内存三种查询算法。

ip2region

数据聚合了一些知名 ip 到地名查询提供商的数据,这些是他们官方的的准确率,经测试,着实比经典的纯真 IP 定位准确一些。ip2region 的数据聚合自以下服务商的开放 API 或者数据。

  • 80%, 淘宝IP地址库, http://ip.taobao.com/

  • ≈10%, GeoIP, https://geoip.com/

  • ≈2%, 纯真IP库, http://www.cz88.net/

备注:如果上述开放API或者数据都不给开放数据时ip2region将停止数据的更新服务。

每条 ip 数据段都固定了格式:

_城市Id|国家|区域|省份|城市|ISP_

只有中国的数据精确到了城市,其他国家有部分数据只能定位到国家,后前的选项全部是 0,已经包含了全部你能查到的大大小小的国家

生成的数据库文件 ip2region.db 只有几 MB,最小的版本只有 1.5MB,随着数据的详细度增加数据库的大小也慢慢增大,目前还没超过 8MB

内置的三种查询算法

全部的查询客户端单次查询都在 0.x 毫秒级别,内置了三种查询算法

  • memory 算法:整个数据库全部载入内存,单次查询都在0.1x毫秒内,C语言的客户端单次查询在0.00x毫秒级别。

  • binary 算法:基于二分查找,基于ip2region.db文件,不需要载入内存,单次查询在0.x毫秒级别。

  • b-tree 算法:基于btree算法,基于ip2region.db文件,不需要载入内存,单词查询在0.x毫秒级别,比binary算法更快。

ip2region安装

下面,就让我们给项目引入 ip2region,进行 ip 信息转换吧

首先引入 maven 依赖

<dependency><groupId>org.lionsoul</groupId><artifactId>ip2region</artifactId><version>1.7.2</version>
</dependency>

然后编写一个工具类 IpUtils ,首先需要加载 ip2region.db 文件

static {dbPath = createFtlFileByFtlArray() + "ip2region.db";try {config = new DbConfig();} catch (DbMakerConfigException e) {e.printStackTrace();}try {searcher = new DbSearcher(config, dbPath);} catch (FileNotFoundException e) {e.printStackTrace();}
}

在加载的时候,需要下载仓库中的 ip2region.db 文件,然后放到 resource 目录下

ip文件

然后,通过内置的三种算法,分别转换用户 ip 地址

    public static String getCityInfo(String ip) {if (StringUtils.isEmpty(dbPath)) {log.error("Error: Invalid ip2region.db file");return null;}if(config == null || searcher == null){log.error("Error: DbSearcher or DbConfig is null");return null;}//查询算法//B-tree, B树搜索(更快)int algorithm = DbSearcher.BTREE_ALGORITHM;//Binary,使用二分搜索//DbSearcher.BINARY_ALGORITHM//Memory,加载内存(最快)//DbSearcher.MEMORY_ALGORITYMtry {// 使用静态代码块,减少文件读取操作
//            DbConfig config = new DbConfig();
//            DbSearcher searcher = new DbSearcher(config, dbPath);//define the methodMethod method = null;switch (algorithm) {case DbSearcher.BTREE_ALGORITHM:method = searcher.getClass().getMethod("btreeSearch", String.class);break;case DbSearcher.BINARY_ALGORITHM:method = searcher.getClass().getMethod("binarySearch", String.class);break;case DbSearcher.MEMORY_ALGORITYM:method = searcher.getClass().getMethod("memorySearch", String.class);break;default:}DataBlock dataBlock = null;if (Util.isIpAddress(ip) == false) {System.out.println("Error: Invalid ip address");}dataBlock = (DataBlock) method.invoke(searcher, ip);String ipInfo = dataBlock.getRegion();if (!StringUtils.isEmpty(ipInfo)) {ipInfo = ipInfo.replace("|0", "");ipInfo = ipInfo.replace("0|", "");}return ipInfo;} catch (Exception e) {e.printStackTrace();}return null;}

下面,我们编写 main 函数进行测试,发现可以正常的解析出 ip 信息

ip信息获取测试

由于 ip 属地在国内的话,只会展示省份,而国外的话,只会展示国家。所以我们还需要对这个方法进行一下封装,得到获取 IP 属地的信息。

/*** 获取IP属地* @param ip* @return*/
public static String getIpPossession(String ip) {String cityInfo = getCityInfo(ip);if (!StringUtils.isEmpty(cityInfo)) {cityInfo = cityInfo.replace("|", " ");String[] cityList = cityInfo.split(" ");if (cityList.length > 0) {// 国内的显示到具体的省if ("中国".equals(cityList[0])) {if (cityList.length > 1) {return cityList[1];}}// 国外显示到国家return cityList[0];}}return "未知";
}

下面,我们在找一个 国外的 IP 测试一下效果。可以看到已经能够正常的显示 IP 属地信息了~

ip属地信息获取测试

到这里如果获取用户的 IP 属地已经完成啦,如果想要了解关于更多 ip2region 的功能,欢迎访问其 Github 地址进行学习。

全网显示 IP 归属地,可以考虑这个开源库相关推荐

  1. 全网显示 IP 归属地,用上这个开源库,实现也太简单了

    细心的小伙伴可能会发现,最近蘑菇新上线了 IP 属地的功能,小伙伴在发表动态.发表评论以及聊天的时候,都会显示自己的 IP 属地信息 ​ 编辑切换为居中 添加图片注释,不超过 140 字(可选) 动态 ...

  2. 再见ip.taobao,全网显示 IP 归属地,用上这个开源库,实现也太简单了!

    细心的朋友应该会发现,最近,继新浪微博之后,头条.腾讯.抖音.知乎.快手.小红书等各大平台陆陆续续都上线了"网络用户IP地址显示功能",境外用户显示的是国家,国内的用户显示的省份, ...

  3. 公众号显示IP归属地,有多少人会现出原形?

    假期吃了一个大瓜,著名的大V连岳翻车,因为微信公众号文章发表于日本被骂上热搜. 网友群嘲他"身在日本劝人爱中,坐办公室写稿劝人进厂,身为丁克劝人三胎". 还有很多假装在国内/国外的 ...

  4. 全网进入“IP归属地”模式,键盘侠老实了,这些人也慌了

    最近一段比较火的事情可能就是全网进入"IP归属地"模式这件事了,其中包括某音.某手.某乎等等一系列自媒体平台,官方的每一次动作都会有一些人起飞,同样,也会有一部分人发慌了. 起飞的 ...

  5. 为何这些软件要显示ip归属地

    4 月 15 日消息,抖音安全中心近日宣布,为维护真实有序的讨论氛围,减少冒充热点事件当事人.恶意造谣.蹭流量等不良行为,抖音拟在个人主页等位置展示帐号 IP 属地,相关功能在 4 月底进行测试,将根 ...

  6. 手把手教你实现显示IP归属地

    手把手教你实现显示IP归属地 实现原理 获取IP地址 调用IP归属地接口: 近年来,根据<国家互联网用户账号名称信息管理规定>要求互联网用户账号服务平台标注IP地址属地信息,微信公众号.抖 ...

  7. 显示IP属地之后,有的人慌了!而有的人却发了?

    最近,国内各大互联网平台根据规定推出了显示IP属地的功能,就如照妖镜一样,让一些人慌了. 比如一些分享海外生活的博主,好多都显示IP属地在国内: 一些国内的本地生活媒体账号也同样翻车: 这么多本地生活 ...

  8. 【实战篇】| 模拟 20 万数据快速查询 IP 归属地

    点击"小詹学Python",选择"置顶"公众号 重磅干货,第一时间送达 本文转载自一个不甘平凡的码农 这篇文章主要深入数据结构与算法在解决实际问题怎么运用和分析 ...

  9. php查询ip归属地api接口_【php】利用新浪api接口与php获取远程数据的方法,获取IP地址,并获取相应的IP归属地...

    本文与<[Servlet]Javaweb中,利用新浪api接口,获取IP地址,并获取相应的IP归属地>(点击打开链接)为姊妹篇,只是后端编程语言换成了php. 做出同样的效果,打开页面,得 ...

最新文章

  1. 智能车竞赛技术报告 | 智能车视觉 - 扬州大学 - 这辆盲车不吃水果
  2. [BZOJ 2756] 奇怪的游戏
  3. Apache Spark源码走读之6 -- 存储子系统分析
  4. zabbix设置邮件报警
  5. 手动创建servlet
  6. 2021年Q2母婴行业季度洞察报告
  7. js正则表达式详细教程
  8. CSS笔记 - fgm练习 2-10 - 提示框效果 (清除子元素浮动高度塌陷的影响)
  9. 未来五年,谁将挑战德国太阳能擂主
  10. 计算机三角函数习惯原创的音乐,三角函数 UNIT版
  11. java 人脸识别 性别识别
  12. PS制作加载GIF图片教程
  13. 生物信息学在疾病基础研究中的应用
  14. PHP强大自适应OA协同办公系统源码 含CRM客户管理系统+内部聊天工具
  15. 华为云-基于Ambari构建大数据平台
  16. (八)Dubbo常见的错误总结
  17. 大华摄像头网页端控制+web串口(适用任何浏览器,不能用来打我)通讯合集
  18. python pip 设置代理
  19. UDS诊断系列之三 ISO14229协议介绍(下)
  20. Unity 3D 泰课网小球移动 相机跟随

热门文章

  1. 大数据要学习什么知识?大数据学习的内容有哪些?
  2. 小学数学计算机教案模板,【小学数学教案五种课型模板】_小学数学教案模板...
  3. 论内存管理在编译器中的重要性
  4. sap 新建事务_sap 常用事务代码
  5. Computer Design and Architecture Revised and Expanded
  6. 在springboot项目中如何设计UrlFilter过滤器
  7. you can attach the source by clicking attach source below
  8. centos 自定义开机启动service
  9. 基于单片机测量空气粘滞系数方案
  10. UG NX二次开发(C#)-CAM-点击插件自动进入CAM模块