SpringBoot通过ip获取归属地,你应该知道的几种方式。

SpringBoot通过ip获取归属地,你应该知道的几种方式。Hi 大家好 我是抢老婆酸奶的小肥仔

大家好,欢迎来到IT知识分享网。

Hi,大家好,我是抢老婆酸奶的小肥仔。

在日常我们逛网站的时候会发现我们登录后会出现归属地信息,例如:我在广州登录会显示广东广州,有些更加精确的会显示到区县。

SpringBoot通过ip获取归属地,你应该知道的几种方式。

那么我们来看看有哪些方式来获取归属地信息?今天我们来聊一聊。

公共方法:

获取用户ip地址:

/ * @author: jiangjs * @description: 获取当前真实IP * @date: 2022/5/23 16:42 / public class GetUserIP { 
    public static final Logger log = LoggerFactory.getLogger(GetUserIP.class); public static String getUserIpAddress(HttpServletRequest request){ 
    try { 
    String realIp = request.getHeader("X-Real-IP"); String forwardedIp = request.getHeader("X-Forwarded-For"); String clientIp = request.getHeader("Proxy-Client-IP"); String proxyIp = request.getHeader("WL-Proxy-Client-IP"); String httpClientIp = request.getHeader("HTTP_CLIENT_IP"); String xForwardedIp = request.getHeader("HTTP_X_FORWARDED_FOR"); String remoteAddress = InetAddress.getLocalHost().getHostAddress(); if(StringUtils.isNotEmpty(forwardedIp) && !"unKnown".equalsIgnoreCase(forwardedIp)){ 
    //多次反向代理后会有多个Ip值,第一个Ip才是真实Ip int index = forwardedIp.indexOf(","); if(index != -1){ 
    return forwardedIp.substring(0,index); }else{ 
    return forwardedIp; } } return StringUtils.isNoneBlank(realIp) && !"unKnown".equalsIgnoreCase(realIp) ? realIp : StringUtils.isNoneBlank(clientIp) && !"unKnown".equalsIgnoreCase(clientIp) ? clientIp : StringUtils.isNoneBlank(proxyIp) && !"unKnown".equalsIgnoreCase(proxyIp) ? proxyIp : StringUtils.isNoneBlank(httpClientIp) && !"unKnown".equalsIgnoreCase(httpClientIp) ? httpClientIp : StringUtils.isNoneBlank(xForwardedIp) && !"unKnown".equalsIgnoreCase(xForwardedIp) ? xForwardedIp : remoteAddress; }catch (Exception e){ 
    e.printStackTrace(); log.error("获取用户Ip报错:{}",e.getMessage()); } return ""; } } 

1、Ip2Region

Ip2Region:见名知意,就是ip转换成区域。根据码云上的简介,Ip2Region是一个离线IP地址定位库和IP定位数据管理框架,能够10微妙级别的查询效率,提供众多主流编程语言的xdb数据生成和查询客户端实现。

1.1 特性

其主要特性有以下几点:

1.1.1 IP数据管理框架

xdb 支持亿级别的 IP 数据段行数,默认的 region 信息都固定了格式:国家|区域|省份|城市|ISP,缺省的地域信息默认是0。 region 信息支持完全自定义,我们可以按照自己的需求来管理IP定位数据。

1.1.2 数据去重和压缩

xdb 格式生成程序会自动去重和压缩部分数据,默认的全部 IP 数据,生成的 ip2region.xdb 数据库是 11MiB,随着数据的详细度增加数据库的大小也慢慢增大。

1.1.3 极速查询响应

基于xdb文件查询单次响应时间在10微妙级别,可以通过以下添加内存的方式加速查询:

  1. vIndex 索引缓存 :使用固定的 512KiB 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作,保持平均查询效率稳定在10-20微秒之间。
  2. xdb 整个文件缓存:将整个 xdb 文件全部加载到内存,内存占用等同于 xdb 文件大小,无磁盘 IO 操作,保持微秒级别的查询效率。

1.2 代码实现

1.2.1 引入jar包
<dependency> <groupId>org.lionsoul</groupId> <artifactId>ip2region</artifactId> <version>2.6.5</version> </dependency> 
1.2.2 封装工具
/ * @author: jiangjs * @description: 创建Ip2Region工具类 * @date: 2022/7/28 14:31 / @Component public class Ip2RegionUtil { 
    public static final Logger log = LoggerFactory.getLogger(Ip2RegionUtil.class); / * 将整个库进行缓存到内存,基于这个库创建查询对象来实现基于文件的查询 * 获取Searcher */ private Searcher getSearcher() throws IOException { 
    //获取地址下的库数据 byte[] bytes = Searcher.loadContentFromFile("D:\ipchange\ip2region\ip2region.xdb"); return Searcher.newWithBuffer(bytes); } / * 根据ip地址直接返回国家、省、城市信息 * @param ip ip * @return 返回地址 */ public String changeIpToAddress(String ip){ 
    //获取searcher Searcher searcher = null; try { 
    searcher = getSearcher(); return searcher.search(ip); }catch (Exception e){ 
    log.error(String.format("ip转地址报错:%s",e.getMessage())); e.printStackTrace(); return ""; }finally { 
    try { 
    if (Objects.nonNull(searcher)){ 
    searcher.close(); } }catch (Exception e){ 
    e.printStackTrace(); } } } } 

ip2region.xdb:为ip对应地址的xdb文件,这个需要不定期的进行更新,更新方法可参考ip2region的码云地址。

1.3 测试

我们提供两个方法,一个方法直接获取ip地址,另一个通过ip138获取到的ip地址,传入到方法进行归属地的查询。

@RestController @RequestMapping("/ip2region") public class Ip2RegionChangeController { 
    @Autowired private Ip2RegionUtil regionUtil; @GetMapping("/getIpToAddress.do") public ResultUtil<String> getIpToAddressNoIp(HttpServletRequest request){ 
    String userIp = GetUserIP.getUserIpAddress(request); return ResultUtil.success(regionUtil.changeIpToAddress(userIp)); } @GetMapping("/getIpToAddress.do/{ip}") public ResultUtil<String> getIpToAddress(@PathVariable("ip") String ip){ 
    return ResultUtil.success(regionUtil.changeIpToAddress(ip)); } } 

直接在浏览器上进行访问:

1、直接访问

SpringBoot通过ip获取归属地,你应该知道的几种方式。

2、通过ip138查询的IP

SpringBoot通过ip获取归属地,你应该知道的几种方式。

通过测试的结果我们可以看到,直接获取到内网的地址并没有解析出地址,而是直接提示了内网。而通过ip138查询到外网地址,能够正常的解析出地址。

2、geoip2

geopip2:是国外的,一个只有100kb的数据库,小巧实用,加载时间极短,查询效率高,项目只包含大陆用户需要的中国IP地址段。geopip2是免费的,里面既包含地理信息,同时也包含了经纬度。GeoIP依赖MaxMind的IP数据,需要频繁更新。

大家有兴趣的话可以直接取git上看看介绍。

2.1 代码实现

2.1.1 引入jar包
<dependency> <groupId>com.maxmind.geoip2</groupId> <artifactId>geoip2</artifactId> <version>2.8.1</version> </dependency> 
2.1.2 封装工具
/ * @author: jiangjs * @description: * @date: 2022/5/23 15:12 / public class GeoLiteUtil { 
    public static final Logger log = LoggerFactory.getLogger(GeoLiteUtil.class); / * 获取ip所在国家 * @param ip 需查询的IP * @return 返回查询结果 * @throws UnknownHostException 异常 */ private static String getCountry(String ip) throws Exception { 
    DatabaseReader reader = getDatabaseReader(); return Objects.nonNull(reader) ? reader.city(InetAddress.getByName(ip)).getCountry().getNames().get("zh-CN") : ""; } / * 获取ip所在省份 * @param ip 需查询的IP * @return 返回查询结果 * @throws UnknownHostException 异常 */ private static String getProvince(String ip) throws Exception { 
    DatabaseReader reader = getDatabaseReader(); return Objects.nonNull(reader) ? reader.city(InetAddress.getByName(ip)).getMostSpecificSubdivision().getNames().get("zh-CN") : ""; } / * 获取ip所在市 * @param ip 需查询的IP * @return 返回查询结果 * @throws UnknownHostException 异常 */ private static String getCity(String ip) throws Exception { 
    DatabaseReader reader = getDatabaseReader(); return Objects.nonNull(reader) ? reader.city(InetAddress.getByName(ip)).getCity().getNames().get("zh-CN") : ""; } / * 获取ip所在经纬度 * @param ip 需查询的IP * @return 返回查询结果 * @throws UnknownHostException 异常 */ private static JSONObject getLongitudeAndLatitude (String ip) throws Exception { 
    DatabaseReader reader = getDatabaseReader(); JSONObject resJson = new JSONObject(); resJson.put("latitude",0d); resJson.put("longitude",0d); if (Objects.nonNull(reader)){ 
    //获取纬度信息 Double latitude = reader.city(InetAddress.getByName(ip)).getLocation().getLatitude(); //获取经度信息 Double longitude = reader.city(InetAddress.getByName(ip)).getLocation().getLongitude(); resJson.put("latitude",latitude); resJson.put("longitude",longitude); } return resJson; } private static DatabaseReader getDatabaseReader(){ 
    InputStream stream = null; try { 
    stream = GeoLiteUtil.class.getClassLoader().getResourceAsStream("static/geo/GeoLite2-City.mmdb"); return new DatabaseReader.Builder(stream).build(); }catch (Exception e){ 
    e.printStackTrace(); log.error("获取geolite2数据库报错:{}",e.getMessage()); }finally { 
    try { 
    if (Objects.nonNull(stream)){ 
    stream.close(); } }catch (Exception e){ 
    e.printStackTrace(); } } return null; } / * 根据Ip获取归属信息 * @param ip 用户Ip * @return 返回查询结果 * @throws Exception 异常 */ public static JSONObject getIpToAddress(String ip) throws Exception { 
    StringJoiner address = new StringJoiner(","); address.add(getCountry(ip)); address.add(getProvince(ip)); address.add(getCity(ip)); JSONObject resJson = GeoLiteUtil.getLongitudeAndLatitude(ip); resJson.put("address",String.valueOf(address)); return resJson; } } 

getDatabaseReader() :读取本地的GeoLite2-City.mmdb并创建DatabaseReader对象。后续使用这个对象来根据Ip读取国家、省级、市级、经纬度等信息。

2.2 测试

测试也跟ip2region一样,提供两个方法,一个测试内网,一个传递外网进行测试。

@RestController @RequestMapping("/change/ip") public class GeoIpChangeController { 
    @Resource private GeoIpChangeService geoIpChangeService; @GetMapping("/getIpAddress.do/{ip}") public ResultUtil<JSONObject> getIpAddress(@PathVariable("ip") String ip, HttpServletRequest request){ 
    try { 
    return ResultUtil.success(GeoLiteUtil.getIpToAddress(ip)); }catch (Exception e){ 
    e.printStackTrace(); } return ResultUtil.error("获取ip对应地址等信息报错"); } @GetMapping("/getLocationIpAddress.do") public ResultUtil<JSONObject> getLocationIpAddress( HttpServletRequest request){ 
    String userIp = GetUserIP.getUserIpAddress(); try { 
    return ResultUtil.success(GeoLiteUtil.getIpToAddress(userIp)); }catch (Exception e){ 
    e.printStackTrace(); } return ResultUtil.error("获取ip对应地址等信息报错"); } } 

直接在浏览器上进行访问:

1、直接访问

SpringBoot通过ip获取归属地,你应该知道的几种方式。

SpringBoot通过ip获取归属地,你应该知道的几种方式。

2、通过外网查询的IP

SpringBoot通过ip获取归属地,你应该知道的几种方式。

通过测试的结果我们可以看到,在解析内网ip时直接报错,提示数据库中没有这个ip。而通过外网地址就能够正常的解析出地址。

3、页面抓取

网络上有很多这种根据ip查询归属地的网站,因此我们可以通过调用其接口,通过返回页面来抓取归属地信息。例如:我们常用的ip138就有可以抓取到。

抓取页面就需要解析页面,解析页面我们可以使用jsoup。

3.1 引入jar包

<dependency> <groupId>org.jsoup</groupId> <artifactId>jsoup</artifactId> <version>1.15.2</version> </dependency> 

3.2 封装方法

/ * @author: jiangjs * @description: * @date: 2023/9/20 16:12 / public class Ip138ChangeUtil { 
    private static final String URL = "http://www.ip138.com/iplookup.asp?ip="; private static final Logger log = LoggerFactory.getLogger(Ip138ChangeUtil.class); public static JSONObject getIpToAddress(String ip) throws IOException { 
    JSONObject resJson = new JSONObject(); Document document = Jsoup.connect(URL + ip + "&action=2").get(); Elements es = document.select("script").eq(1); for (Element e : es) { 
    String[] data = e.data().split("var"); for (String variable : data) { 
    if (variable.contains("=") && variable.contains("ip_result")){ 
    String[] va = StringUtils.split(variable, "="); String value = va[1].trim().substring(0,va[1].trim().length() - 1); JSONObject object = JSONObject.parseObject(value); log.info(String.format("获取到的数据值:%s",value)); resJson.put("address", object.getString("ASN归属地")); } } } return resJson; } } 

工具类中通过访问地址获取到Document对象,然后在进行解析,从而拿到归属地信息。

3.3 测试

@RestController @RequestMapping("/ip138") public class Ip138ChangeController { 
    @GetMapping("/getIpToAddress.do/{ip}") public ResultUtil<JSONObject> getIpToAddress(@PathVariable("ip") String ip){ 
    try { 
    return ResultUtil.success(Ip138ChangeUtil.getIpToAddress(ip)); } catch (IOException e) { 
    e.printStackTrace(); return ResultUtil.error("查询失败"); } } } 

直接访问地址:

SpringBoot通过ip获取归属地,你应该知道的几种方式。

从结果来看,其可以精确到区的。

【总结】

上述中介绍了三种通过Ip获取归属地的方法,相比较而言:

ip2region相对用的比较多,很多博客、技术文档都会进行介绍,而且封装使用也比较简单,执行速度也比较快;

geoip2:相对比较细,可以根据需求直接获取国家,省级等信息,但是伴随的就是封装稍微复杂点,而且在解析内网时,由于数据库中没有对应ip而报错。

页面抓取:这个就不推荐了,毕竟依赖于第三方,如果服务挂了的话就没法使用了。

至于选择ip2region还是geoip2,这个就要看具体的也无需求。

对了,ip2region和geoip2要去经常更新库哦。

好了,今天就跟大家叨叨到这,谢谢大家。

码云地址:https://gitee.com/lovequeena/iptoaddress.git

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/147518.html

(0)
上一篇 2025-04-06 21:10
下一篇 2025-04-06 21:15

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信