Java获取用户IP属地模拟抖音详解

目录
  • 介绍
  • 内置的三种查询算法
  • ip2region安装

介绍

细心的小伙伴可能会发现,抖音新上线了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信息了

我在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

到此这篇关于Java获取用户IP属地模拟抖音详解的文章就介绍到这了,更多相关Java IP属地内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • golang爬虫colly 发送post请求

    继续还是工作中使用colly,不管是官网,还是网上的一些文章(其实90%就是把官网的案例抄过去),都是一样的格式,没有讲到post,测试了几次,记录一下post的使用     c := colly.NewCollector()     type data struct {         Phone string `json:"phone" binding:"required"`     }     d:=&data{         Phone:"

  • java调用淘宝api联网查询ip归属地

    淘宝返回的数据为:{"code":0,"data":{"country":"\u4e2d\u56fd","country_id":"CN","area":"\u534e\u4e1c","area_id":"300000","region":"\u5c71\u4e1c\u7701&

  • Java获取用户IP属地模拟抖音详解

    目录 介绍 内置的三种查询算法 ip2region安装 介绍 细心的小伙伴可能会发现,抖音新上线了IP属地的功能,小伙伴在发表动态.发表评论以及聊天的时候,都会显示自己的IP属地信息 下面,我就来讲讲,Java中是如何获取IP属地的,主要分为以下几步 通过 HttpServletRequest 对象,获取用户的IP地址 通过 IP 地址,获取对应的省份.城市 首先需要写一个IP获取的工具类,因为每一次用户的Request请求,都会携带上请求的IP地址放到请求头中. public class Ip

  • 利用JavaScript获取用户IP属地方法详解

    目录 写在前面 尝试一:navigator.geolocation 尝试二:sohu 的接口 尝试三:百度地图的接口 写在后面 写在前面 想要像一些平台那样显示用户的位置信息,例如某省市那样.那么这是如何做到的, 据说这个位置信息的准确性在通信网络运营商那里?先不管,先实践尝试下能不能获取. 尝试一:navigator.geolocation 尝试了使用 navigator.geolocation,但未能成功拿到信息. getGeolocation(){ if ('geolocation' in

  • 微信小程序获取用户信息并保存登录状态详解

    前言 微信小程序的运行环境不是在浏览器下运行的.所以不能以cookie来维护登录态.下面我就来说说我根据官方给出的方法来写出的维护登录态的方法吧. 一.登录态维护 官方的文档地址:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html#wxloginobject 通过 wx.login() 获取到用户登录态之后,需要维护登录态.开发者要注意不应该直接把 session_key.openid 等字段作为用户的标识或者 session

  • SpringSecurity从数据库中获取用户信息进行验证的案例详解

    基于 SpringBoot与SpringSecurity整合案例的修改: 数据库 user 表 注,密码是由 BCrypt 算法加密对应用户名所得. root $2a$10$uzHVooZlCWBkaGScKnpha.ZrK31NI89flKkSuTcKYjdc5ihTPtPyq blu $2a$10$mI0TRIcNF4mg34JmH6T1KeystzTWDzWFNL5LQmmlz.fHndcwYHZGe kaka $2a$10$/GMSSJ3AzeeBK3rBC4t8BOZ5zkfb38Il

  • java数据结构与算法数组模拟队列示例详解

    目录 一.什么是队列 二.用数组来模拟队列 一.什么是队列 队列是一个有序列表,可以用数组或者链表来实现. 遵循先入先出的原则,即:先存入队列的数据,要先取出.后存入的的数据,后取出. 看一张队列的模拟图,1,2,3表示同一个队列Queue.在队列中有2个指针,front表示队首,rear表示队尾. 图1中表示队列里还没有数据,所以front跟rear初始化都是-1. 当图2中有数据进行存入的时候,front没变,而rear则随着数据的增多而改变.存入了4个数据,于是rear=3. 再看图3,f

  • 微信小程序授权获取用户详细信息openid的实例详解

    小程序获取用户的头像昵称openid之类 第一种使用wx.getUserInfo直接获取微信头像,昵称 wx.getUserInfo({ success: function (res) { that.setData({ nickName: res.userInfo.nickName, avatarUrl: res.userInfo.avatarUrl, }) }, }) 第二种 我们在使用小程序wx.login API进行登录的时候,直接使用wx.getUserInfo是不能获取更多的信息的,如

  • java 获取request中的请求参数代码详解

    1.get 和 post请求方式 (1)request.getParameterNames(); 获取所有参数key后.遍历request.getParameter(key)获取value (2)request.getParameterMap() .直接包含参数key和value值,简单方便 Map<String, String[]>maps = request.getParameterMap(); for (Map.Entry<String, String[]> entry :

  • 微信公众平台开发教程①获取用户Openid及个人信息图文详解

    本文实例讲述了微信公众平台开发获取用户Openid及个人信息.分享给大家供大家参考,具体如下: 前言: 初次尝试微信公众号的开发,对于学习方法的探索都是来源于网上的博客.问答,对于参差不齐的信息,自己也是有苦说不出,抽出一点时间写点文章,既是对自己的学习总结,也希望给予同是菜鸟的学渣一点帮助 背景介绍: 我需要用户接收微信分享的链接后,点击进入给参加活动的用户[点赞],然后需要后台获取该微信用户的 openid 作为唯一的标记信息,以便保证该用户下次进入后进行数据库的比对,直接提取其对应的操作信

  • 小程序获取用户信息的两种方法详解(getUserProfile和头像昵称填写)

    目录 第一种使用 getUserProfile 第二种使用 头像昵称填写 总结 相信大家之前也经常使用open-data获取用户的头像和昵称吧,但微信的这个改编意味着我们要使用新的方法获取信息了.在讨论区引发了很大的讨论,接下来我们一起尝试两种获取信息的方法. 第一种使用 getUserProfile 我们可以查看一下官方文档 wx.getUserProfile(Object object),获取用户信息.页面产生点击事件(例如 button 上 bindtap 的回调中)后才可调用,每次请求都

  • Java获取接口所有实现类的方式详解

    有时候,根据业务逻辑的需求,我们想要获取到某个接口的所有实现类.在这里大致介绍两种方式: 1.借助Spring容器实现 Spring作为一个容器,管理着一个项目中所有经过配置的Java类(xml配置文件或Annotation方式).如果某个接口的所有实现类均被Spring托管了,那么通过Spring就可以很简单的返回这些实现类. import org.springframework.beans.BeansException; import org.springframework.context.

随机推荐