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

news2024/11/19 19:29:52

细心的小伙伴可能会发现,最近蘑菇新上线了 IP 属地的功能,小伙伴在发表动态、发表评论以及聊天的时候,都会显示自己的 IP 属地信息

动态显示IP属地

在蘑菇群聊中,也 可 以 展 示 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")) {
                // 根据网卡取本机配置的IP
                try {
                    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/

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

原来的请求方式如下

/**
 * 获取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/ip2region
    String 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# 等查询绑定和Binary,B树,内存三种查询算法。

​数据聚合了一些知名 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 地址

  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_ALGORITYM
        try {
            // 使用静态代码块,减少文件读取操作
//            DbConfig config = new DbConfig();
//            DbSearcher searcher = new DbSearcher(config, dbPath);

            //define the method
            Method 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属地
 * @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 属地已经完成啦,如果想要了解关于更多 ip2region 的功能,欢迎访问其 Github 地址进行学习。

项目地址

https://github.com/lionsoul2014/ip2region

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【强化学习论文合集】十八.2019国际表征学习大会论文(ICLR2019)

强化学习(Reinforcement Learning, RL),又称再励学习、评价学习或增强学习,是机器学习的范式和方法论之一,用于描述和解决智能体(agent)在与环境的交互过程中通过学习策略以达成回报最大化或实现特定目标的问题。 本专栏整理了近几年国际顶级会议中,涉及强化学习(Rein…

面试官:你觉得你最大的缺点是什么?

面试官:你觉得你最大的缺点是什么? 前言 相信百分之80的同学们都会被问到这个问题&#xff1a;你觉得你最大的缺点是什么? 这也是求职者一个充满恐惧的问题&#xff0c;特别是我们程序员&#xff0c;大家在与人沟通并没有我们与代码沟通这么得心应手&#xff0c;如果你没回…

网络请求工具wget和curl

一. wget命令 wget命令来自于英文词组”web get“的缩写&#xff0c;其功能是用于从指定网址下载网络文件。 wget命令支持如HTTP、HTTPS、FTP等常见协议&#xff0c;可以在命令行中直接下载网络文件。 注意&#xff1a;不同busybox版本集成的wget命令&#xff0c;可能不…

统计检验分析

1. 正态分布检验 2. 统计检验 正态分布且方差齐非正态分布或方差不齐para test non-para testnon-pairedpaired2组 t-testWilcoxon rank-sum testWilcoxon signed-rank test3组及以上One way ANOVA Kruskal-Wallis testt-test: Paired t-test: 确定某个总体的成对测量值之间…

说说WM_DESTROY和WM_NCDESTROY的区别

在一个 Windows 窗口被销毁的时候&#xff0c;你会发现有两个比较类似的消息&#xff1a;WM_DESTROY和WM_NCDESTROY&#xff0c;那么&#xff0c;这俩兄弟之间有什么区别呢&#xff1f;今天就来讲讲。 不同之处在于&#xff0c;WM_DESTROY消息在窗口销毁序列的开头发送&#x…

MySQL执行计划误选索引及修改方案

MySQL的优化器 MySQL在执行查询语句时使用那个索引是由server层的优化器决定的。优化器的作用是找到一个最优的执行方案&#xff0c;用最小的代价去执行语句。由于MySQL使用预估的方式去选择索引&#xff0c;所以MySQL可能会出现选择索引出错的情况&#xff0c;无法命中最优索…

刘韧工作手册(2023年版)

刘韧于2022年9月22日为云算科技做内部演讲。由谭缘整理成文&#xff0c;李欣欣编辑&#xff0c;朱芳文审定。一、认知篇01 干中学&#xff0c;重复做。“学”是为了“习”&#xff0c;学到的东西是为了下一次习的时候&#xff0c;做得更好。“习”&#xff0c;是最终实践的成果…

WSL安装教程

wsl安装教程引言前期准备工作安装wsl第一步第二步 检测系统版本第三步 确定虚拟机特性第四步 下载Linux内核的更新包第五步 设置WSL 2作为默认版本第六步 选择Linux发行版本并设置Linux账号小TIPS引言 Windows Subsystem for Linux&#xff08;简称WSL&#xff09;是一个在Win…

大家都在画圣诞树,我们用代码敲一颗吧~圣诞树

前段时间发布的文章很多人问怎么操作的&#xff0c;今天具体说明一下&#xff1a;PS&#xff1a;如果需要下载可以点击左下角阅读全文下载代码使用更方便具体步骤如下&#xff1a;复制下面代码在电脑里面新建一个记事本&#xff0c;将代码复制到新建的记事本里保存记事本&#…

虚拟机网络连通性选择

做运维的朋友对于虚拟机这个概念应该不会陌生&#xff0c;这里不做讲解。今天主要想对虚拟机的网络连通性的选择方法做一个简单的介绍&#xff0c;如果是老人就没必要看了&#xff0c;此文章针对刚入门初次使用虚拟机进行测试、工作的小伙伴。 咱们常见的虚拟机平台软件有很多&…

【实时数仓】用户行为日志采集模块单机模式部署,Nginx介绍、安装和配置,采集模块集群部署并使用Nginx进行反向代理

文章目录一 日志采集模块1 打包单机部署&#xff08;1&#xff09;修改gmall2022-logger中的logback.xml配置文件&#xff08;2&#xff09;修改SpringBoot核心配置文件application.propeties&#xff08;3&#xff09;测试&#xff08;4&#xff09;程序运行流程2 Nginx&#…

Grafana 的介绍和安装

版本&#xff1a;9.3.1 介绍 Grafana是一款能够提供查询、告警和可视化指标、日志、链路跟踪的软件&#xff0c;并且提供了TSDB时序数据库用于存储数据。 一共有3个版本&#xff0c;Grafana OSS&#xff08;开源版&#xff09;&#xff0c;Grafana Enterprise&#xff08;企业…

【C++】红黑树

一.红黑树的概念与性质 1.概念 红黑树是二叉搜索数的一种, 相比于AVL树(二叉平衡搜索树)红黑树通过减少旋转的次数来进一步优化了查找效率, 在每个节点上增加一个存储位表示节点的颜色, Red or Black, 通过对任何一条从根到叶子的路径上各个节点着色方式的限制, 红黑树确保没…

亚马逊云科技re:Invent:创新永无止境,科技重塑未来

作为全球IT行业一年一度的科技盛宴&#xff0c;2022亚马逊云科技re:Invent全球大会吸引了来自世界各国的开发者与企业客户的高度关注&#xff0c;其中自然也包括了广大中国企业。作为全球云计算领域的风向标&#xff0c;本届大会究竟揭示了怎样的行业趋势&#xff1f;发布了哪些…

【超聚变】FusionServer1288H V6安装CentOS系统

【超聚变】FusionServer1288H V6安装CentOS系统1、创建Raid2、安装系统2.1.使用IBMC地址&#xff0c;登录web界面2.2.选择启动虚拟控制台2.3.挂载镜像2.4.选择启动盘2.5.安装系统2.6.选择英文安装2.7.时区选择上海&#xff08;Asia/Shanghai&#xff09;2.8.选择最小化安装&…

【Vue源码解析】mustache模板引擎

模板引擎什么是模板引擎实现 Scanner 类根据模板字符串生成 tokens在 index.js 引入 parseTemplateToTokens实现 tokens 的嵌套One More Thingtokens 结合数据解析为 dom 字符串定义 lookup 函数定义 renderTemplate 函数什么是模板引擎 模板引擎是将数据变为视图最优雅的解决…

如何清除chrome浏览器缓存

清除浏览器的缓存知识调用前言引入具体操作知识调用 文章中可能用到的知识点前端学习&#xff1a;浏览器缓存方式有哪些&#xff08;http协议 websql indexDB cookie localstorage sessionstorage&#xff09;如何查看Chrome浏览器的页面缓存内容【详细教程】 前言引入 上期文…

微服务框架 SpringCloud微服务架构 微服务保护 31 限流规则 31.1 簇点链路

微服务框架 【SpringCloudRabbitMQDockerRedis搜索分布式&#xff0c;系统详解springcloud微服务技术栈课程|黑马程序员Java微服务】 微服务保护 文章目录微服务框架微服务保护31 限流规则31.1 簇点链路31.1.1 簇点链路31.1.2 快速入门31 限流规则 31.1 簇点链路 31.1.1 簇…

52、网络

目录 一、网络通信 二、网络 三、 ip地址 四、域名 五、端口号 六、网络通信协议 1、协议&#xff08;tcp/ip&#xff09; 2、TCP和UDP 七、InetAddress类 八、Socket 1、基本介绍&#xff1a; 2、TCP网络通信编程 3、UDP网络通信编程&#xff08;了解即可&#xff0c…

完整版JAVA物业管理系统源码带小程序+文字安装教程+视频

这套系统还包含了小程序前端源码。 技术架构 技术框架&#xff1a;springboot ssm shiro layui 运行环境&#xff1a;IntelliJ IDEA 2022 jdk1.8 Mysql5.7.4 maven nginx 宝塔面板 文字安装教程 1.下载源码后打开小皮面板&#xff0c;安装mysql5.7数据库&#xff0c;创…