SpringBoot轻松实现ip解析(含源码)

这篇具有很好参考价值的文章主要介绍了SpringBoot轻松实现ip解析(含源码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


SpringBoot轻松实现ip解析(含源码),SpringBoot,spring boot,tcp/ip,后端,原力计划

前言

IP地址一般以数字形式表示,如192.168.0.1。IP解析是将这个数字IP转换为包含地区、城市、运营商等信息的字符串形式,如“广东省深圳市 电信”。这样更方便人理解和使用。


应用场景

SpringBoot轻松实现ip解析(含源码),SpringBoot,spring boot,tcp/ip,后端,原力计划

(1)网站访问分析

可以解析用户IP地址,分析网站访问量的地域分布,以便进行针对性推广。

(2)欺诈风险控制

某地区IP地址多次进行恶意刷单、刷票等行为,可以通过IP地址解析加以拦截。

(3)限制服务区域

解析用户IP地址,限制仅为某地区的用户提供服务。

(4)显示访问者来源

在博客、论坛等,解析并显示每个帖子的发帖人来自哪个地区。

下面带大家实践在spring boot 项目中获取请求的ip与详细地址。


示例

前期准备

引用框架:Ip2region

下载地址: https://gitee.com/lionsoul/ip2region.git

注意:如果需要离线获取需要下载 /data/ip2region.xdb 文件

Ip2region 特性
1、IP 数据管理框架

xdb 支持亿级别的 IP 数据段行数,默认的 region 信息都固定了格式:国家|区域|省份|城市|ISP,缺省的地域信息默认是0。 region 信息支持完全自定义,例如:你可以在 region 中追加特定业务需求的数据,例如:GPS信息/国际统一地域信息编码/邮编等。也就是你完全可以使用 ip2region 来管理你自己的 IP 定位数据。

2、数据去重和压缩

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

3、极速查询响应

即使是完全基于 xdb 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:

  1. vIndex 索引缓存 :使用固定的 512KiB 的内存空间缓存 vector index 数据,减少一次 IO 磁盘操作,保持平均查询效率稳定在10-20微秒之间。
  2. xdb 整个文件缓存:将整个 xdb 文件全部加载到内存,内存占用等同于 xdb 文件大小,无磁盘 IO 操作,保持微秒级别的查询效率。
版本依赖
框架名 版本号
SpringBoot 3.1.4
jdk 17
tomcat-embed-core 10.1.8
lombok 1.18.26
fastjson 1.2.73
ip2region 2.7.0
导入库
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

<!--ip库-->
<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>10.1.8</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.26</version>
    <scope>compile</scope>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.73</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
</dependency>
具体代码
Constant
public interface Constant {

    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
    /**
     * 本地ip
     */
    public static final String LOCAL_IP = "127.0.0.1";


    /**
     * 数据库访问地址
     */
    public static final String DB_PATH = "springboot-ip/src/main/resources/ip2region/ip2region.xdb";
}
AddressUtils(在线解析)
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLConnection;

import static com.example.springbootip.constant.Constant.IP_URL;
import static com.example.springbootip.constant.Constant.LOCAL_IP;

/**
 * @author coderJim
 * @date 2023-10-16 14:07
 */
@Slf4j
public class AddressUtils {

    // 未知地址
    public static final String UNKNOWN = "XX XX";

    public static String getRealAddressByIP(String ip) {
        String address = UNKNOWN;
        // 内网不查询
        if (ip.equals(LOCAL_IP)) {
            return "内网IP";
        }
        if (true) {
            try {
                String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true" ,"GBK");
                if (StringUtils.isEmpty(rspStr)) {
                    log.error("获取地理位置异常 {}" , ip);
                    return UNKNOWN;
                }
                JSONObject obj = JSONObject.parseObject(rspStr);
                String region = obj.getString("pro");
                String city = obj.getString("city");
                return String.format("%s %s" , region, city);
            } catch (Exception e) {
                log.error("获取地理位置异常 {}" , ip);
            }
        }
        return address;
    }

    public static String sendGet(String url, String param, String contentType) {
        StringBuilder result = new StringBuilder();
        BufferedReader in = null;
        try {
            String urlNameString = url + "?" + param;
            log.info("sendGet - {}" , urlNameString);
            URL realUrl = new URL(urlNameString);
            URLConnection connection = realUrl.openConnection();
            connection.setRequestProperty("accept" , "*/*");
            connection.setRequestProperty("connection" , "Keep-Alive");
            connection.setRequestProperty("user-agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
            connection.connect();
            in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
            String line;
            while ((line = in.readLine()) != null) {
                result.append(line);
            }
            log.info("recv - {}" , result);
        } catch (ConnectException e) {
            log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
        } catch (SocketTimeoutException e) {
            log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
        } catch (IOException e) {
            log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e);
        } catch (Exception e) {
            log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception ex) {
                log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
            }
        }
        return result.toString();
    }
}
IpUtil(离线解析)
import static com.example.springbootip.constant.Constant.DB_PATH;
import static com.example.springbootip.constant.Constant.LOCAL_IP;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author coderJim
 * @date 2023-10-16 11:02
 */
@Slf4j
public class IpUtil {

    protected IpUtil(){ }

    /**
     * 获取 IP地址
     * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
     * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (ipAddress.equals(LOCAL_IP)) {
                    // 根据网卡取本机配置的IP
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        log.error(e.getMessage(), e);
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
            if (ipAddress != null && ipAddress.length() > 15) { // "***.***.***.***".length()
                // = 15
                if (ipAddress.indexOf(",") > 0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }

        return ipAddress;
    }

    public static  String getAddr(String ip) {
        String project_dir = System.getProperty("user.dir") +"";
        String dbPath = project_dir + "/" + DB_PATH;
        // 1、从 dbPath 加载整个 xdb 到内存。
        byte[] cBuff;
        try {
            cBuff = Searcher.loadContentFromFile(dbPath);
        } catch (Exception e) {
            log.info("failed to load content from `%s`: %s\n", dbPath, e);
            return null;
        }

        // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
        Searcher searcher;
        try {
            searcher = Searcher.newWithBuffer(cBuff);
        } catch (Exception e) {
            log.info("failed to create content cached searcher: %s\n", e);
            return null;
        }
        // 3、查询
        try {
            String region = searcher.search(ip);
            return region;
        } catch (Exception e) {
            log.info("failed to search(%s): %s\n", ip, e);
        }
        return null;
    }

}
IpController
import com.example.springbootip.util.AddressUtils;
import com.example.springbootip.util.IpUtil;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.*;

/**
 * @author coderJim
 */
@RestController
@RequestMapping("/demo")
public class IpController {
    
    @ResponseBody
    @GetMapping("/realAddress")
    public String realAddress(HttpServletRequest request){
        String ip = IpUtil.getIpAddr(request);
        //在线获取
//        String realAddress = AddressUtils.getRealAddressByIP(ip);
        //离线获取
        String realAddress = IpUtil.getAddr(ip);
        return "您的ip所在地为:"+ realAddress;
    }
}
执行结果

运行请求 http://127.0.0.1:8080/demo/realAddress

如果执行:

String realAddress = AddressUtils.getRealAddressByIP(ip);

显示结果:

您的ip所在地为:浙江省 杭州市

如果执行:

String realAddress = IpUtil.getAddr(ip);

显示结果:

您的ip所在地为:中国|0|浙江省|杭州市|电信

总结

通过这样的一套流程下来,我们就能实现对每一个请求进行ip 获取、ip解析

源码获取

​如果需要完整源码请关注公众号"架构殿堂" ,回复 "SpringBoot+ip"即可获得


写在最后

感谢您的支持和鼓励! 😊🙏

如果大家对相关文章感兴趣,可以关注公众号"架构殿堂",会持续更新AIGC,java基础面试题, netty, spring boot, spring cloud等系列文章,一系列干货随时送达!文章来源地址https://www.toymoban.com/news/detail-716978.html

到了这里,关于SpringBoot轻松实现ip解析(含源码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Spring Boot 源码学习】自动装配流程源码解析(上)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家从整体上了解了AutoConfigurationImportSelector 自动装配逻辑的核心功能及流程,由于篇幅有限,更加细化的功能及流程详解还没有介绍。本篇开始将从其源码入手,重点解析细化后的自动装配流程源码。 在开始本篇的内容介绍之前

    2024年02月14日
    浏览(38)
  • 【Spring Boot 源码学习】自动装配流程源码解析(下)

    《Spring Boot 源码学习系列》 上篇博文,笔者带大家了解了自动装配流程中有关自动配置加载的流程; 本篇将介绍自动装配流程剩余的内容,包含了自动配置组件的排除和过滤、触发自动配置事件。 在开始本篇的内容介绍之前,我们先来看看往期的系列文章【有需要的朋友,

    2024年02月11日
    浏览(34)
  • Spring Boot源码解析 - 自动装配原理

    Spring Boot 自动装配是 Spring Boot 框架的一个关键特性,它的目标是让开发者能够快速构建 Spring 应用程序,减少繁琐的配置工作。   @SpringApplication 从启动类 @SpringApplication 注解入手, @SpringBootApplication 是一个组合注解,它是 Spring Boot 框架中常用的一个主要注解之一。它结合了

    2024年01月19日
    浏览(42)
  • 用Spring Boot轻松实现定时任务--原理详解

      在现代化的web开发中,定时任务是一个非常常见的功能。Spring Boot为我们提供了一个简便的方式来处理这些任务,我们只需加入一些注解和配置即可完成。本文将介绍 Spring Boot 定时任务的基本概念和原理,以及如何在具体业务场景中使用和优化配置。   定时任务是指在

    2024年02月06日
    浏览(50)
  • Spring Boot进阶(74):轻松实现高效SOAP服务! Spring Boot与CXF完美结合

            SOAP(简单对象访问协议)是一种基于XML的通信协议,它常用于Web服务的实现。在Java中,Apache CXF是一个流行的实现SOAP的框架,它实现了JAX-WS和JAX-RS标准。Spring Boot是一个快速开发Web应用的框架,它提供了许多自动化的配置和依赖注入的功能。在本文中,我们将要探

    2024年02月06日
    浏览(53)
  • Spring Boot进阶(19):探索ElasticSearch:如何利用Spring Boot轻松实现高效数据搜索与分析

            ElasticSearch是一款基于Lucene的开源搜索引擎,具有高效、可扩展、分布式的特点,可用于全文搜索、日志分析、数据挖掘等场景。Spring Boot作为目前最流行的微服务框架之一,也提供了对ElasticSearch的支持。本篇文章将介绍如何在Spring Boot项目中整合ElasticSearch,并展

    2024年02月11日
    浏览(48)
  • Nginx + Spring Boot 轻松实现负载均衡,还有谁不会??

    来源:虚无境的博客 地址:www.cnblogs.com/xuwujing/p/11953697.html 在介绍Nginx的负载均衡实现之前,先简单的说下负载均衡的分类,主要分为 硬件负载均衡和软件负载均衡 ,硬件负载均衡是使用专门的软件和硬件相结合的设备,设备商会提供完整成熟的解决方案,比如F5,在数据的

    2024年02月08日
    浏览(38)
  • Spring Boot邮件发送教程:步步为营,轻松实现图片附件邮件!

      通过Spring Boot构建一个功能强大的邮件发送应用程序,重点是实现发送包含图片附件的邮件。我将逐步介绍添加必要的依赖、创建邮件服务类和控制器的步骤,并提供了具体的示例源代码。跟随这个简单而清晰的教程,您将能够轻松地集成邮件发送功能到您的Spring Boot应用中

    2024年02月04日
    浏览(51)
  • redisson-spring-boot-starter 自动化配置源码解析

    redisson-spring-boot-starter:3.25.2 此starter会自动注册RedissonClient Bean 并可通过注册RedissonAutoConfigurationCustomizer Bean实现配置自定义 spring-boot:2.7以上 org.redisson.spring.starter.RedissonAutoConfigurationV2 spring-boot:2.6以下 org.redisson.spring.starter.RedissonAutoConfiguration

    2024年01月17日
    浏览(82)
  • Spring Boot入门(04):实现多环境配置文件切换,轻松搞定不同环境部署问题

            在开发和部署Spring Boot应用的过程中,经常需要在不同的环境中进行配置,比如开发环境、测试环境、生产环境等。为了方便管理和部署,我们需要实现多环境配置文件切换。本篇教程将带你轻松搞定不同环境部署问题,让你的应用在各个环境中稳定运行。无论你是

    2024年02月10日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包