• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Spring Boot实现IP地址

武飞扬头像
y_bccl27
帮助1

一、本地解析

如果使用本地ip解析的话,我们将会借助ip2region,该项目维护了一份较为详细的本地ip地址对应表,如果为了离线环境的使用,需要导入该项目依赖,并指定版本,不同版本的方法可能存在差异。

  1.  
    <dependency>
  2.  
    <groupId>org.lionsoul</groupId>
  3.  
    <artifactId>ip2region</artifactId>
  4.  
    <version>2.6.3</version>
  5.  
    </dependency>

在使用时需要将xdb文件下载到resources目录下,ip2region使用完全基于xdb文件的查询,单次查询响应时间在十微秒级别:

学新通 

  1.  
    package com.example.demo.utils;
  2.  
     
  3.  
    import cn.hutool.core.util.StrUtil;
  4.  
    import cn.hutool.json.JSONObject;
  5.  
    import cn.hutool.json.JSONUtil;
  6.  
    import lombok.NoArgsConstructor;
  7.  
    import lombok.extern.slf4j.Slf4j;
  8.  
    import org.lionsoul.ip2region.xdb.Searcher;
  9.  
     
  10.  
    import javax.servlet.http.HttpServletRequest;
  11.  
    import java.io.BufferedReader;
  12.  
    import java.io.IOException;
  13.  
    import java.io.InputStreamReader;
  14.  
    import java.net.ConnectException;
  15.  
    import java.net.InetAddress;
  16.  
    import java.net.SocketTimeoutException;
  17.  
    import java.net.URL;
  18.  
    import java.net.URLConnection;
  19.  
    import java.net.UnknownHostException;
  20.  
    import java.util.ArrayList;
  21.  
    import java.util.List;
  22.  
     
  23.  
    @NoArgsConstructor
  24.  
    @Slf4j
  25.  
    public class IPUtil {
  26.  
     
  27.  
    private static final String UNKNOWN = "unknown";
  28.  
    private static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
  29.  
    private static List<String> internalIpList=new ArrayList<>();
  30.  
    private static byte[] cBuff;
  31.  
     
  32.  
    {
  33.  
    internalIpList.add("192.168.1.105");
  34.  
    internalIpList.add("127.0.0.1");
  35.  
    }
  36.  
     
  37.  
    /**
  38.  
    * 功能:获取IP地址
  39.  
    * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
  40.  
    * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
  41.  
    * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
  42.  
    */
  43.  
    public static String getIp(HttpServletRequest request) {
  44.  
    String ip = request.getHeader("x-forwarded-for");
  45.  
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
  46.  
    ip = request.getHeader("Proxy-Client-IP");
  47.  
    }
  48.  
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
  49.  
    ip = request.getHeader("WL-Proxy-Client-IP");
  50.  
    }
  51.  
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
  52.  
    ip = request.getHeader("HTTP_CLIENT_IP");
  53.  
    }
  54.  
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
  55.  
    ip = request.getHeader("HTTP_X_FORWARDED_FOR");
  56.  
    }
  57.  
    if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
  58.  
    ip = request.getRemoteAddr();
  59.  
    }
  60.  
     
  61.  
    // 本机访问
  62.  
    if ("localhost".equalsIgnoreCase(ip) || "127.0.0.1".equalsIgnoreCase(ip) || "0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)){
  63.  
    // 根据网卡取本机配置的IP
  64.  
    InetAddress inet;
  65.  
    try {
  66.  
    inet = InetAddress.getLocalHost();
  67.  
    ip = inet.getHostAddress();
  68.  
    } catch (UnknownHostException e) {
  69.  
    e.printStackTrace();
  70.  
    }
  71.  
    }
  72.  
     
  73.  
    // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
  74.  
    if (null != ip && ip.length() > 15) {
  75.  
    if (ip.indexOf(",") > 15) {
  76.  
    ip = ip.substring(0, ip.indexOf(","));
  77.  
    }
  78.  
    }
  79.  
    return ip;
  80.  
    }
  81.  
     
  82.  
    public static String getIpAddrByLocal(String ip) {
  83.  
    // 1、创建一个完全基于文件的查询对象
  84.  
    String xdbPath = "src/main/resources/ip2region.xdb";
  85.  
    Searcher searcher;
  86.  
    try {
  87.  
    searcher = Searcher.newWithFileOnly(xdbPath);
  88.  
    }catch (Exception e) {
  89.  
    log.error("无法创建内存的查询对象Searcher");
  90.  
    return null;
  91.  
    }
  92.  
     
  93.  
    // 2、查询
  94.  
    try {
  95.  
    return searcher.searchByStr(ip);
  96.  
    } catch (Exception e) {
  97.  
    log.error("IP地址位置查询失败(%s):%s\n",ip, e);
  98.  
    }
  99.  
    return null;
  100.  
    }
  101.  
     
  102.  
    public static String getIpAddrByByOnline(String ip) {
  103.  
    String address = UNKNOWN;
  104.  
    if (internalIp(ip)) {
  105.  
    // 判断是否是内网,如果是内网,则不进行查询,直接返回
  106.  
    return "内网IP";
  107.  
    }
  108.  
    if (true) {
  109.  
    try {
  110.  
    String rspStr = sendGet(IP_URL, "ip=" ip "&json=true" ,"GBK");
  111.  
    if (StrUtil.isBlank(rspStr)) {
  112.  
    log.error("获取地理位置异常 {}" , ip);
  113.  
    return UNKNOWN;
  114.  
    }
  115.  
    JSONObject obj = JSONUtil.parseObj(rspStr);
  116.  
    String region = obj.getStr("pro");
  117.  
    String city = obj.getStr("city");
  118.  
    return String.format("%s %s" , region, city);
  119.  
    } catch (Exception e) {
  120.  
    log.error("获取地理位置异常:{}",ip);
  121.  
    }
  122.  
    }
  123.  
    return address;
  124.  
    }
  125.  
     
  126.  
    public static String sendGet(String url, String param, String contentType) {
  127.  
    StringBuilder result = new StringBuilder();
  128.  
    BufferedReader in = null;
  129.  
    try {
  130.  
    String urlNameString = url "?" param;
  131.  
    log.info("sendGet - {}" , urlNameString);
  132.  
    URL realUrl = new URL(urlNameString);
  133.  
    URLConnection connection = realUrl.openConnection();
  134.  
    connection.setRequestProperty("accept" , "*/*");
  135.  
    connection.setRequestProperty("connection" , "Keep-Alive");
  136.  
    connection.setRequestProperty("user-agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
  137.  
    connection.connect();
  138.  
    in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType));
  139.  
    String line;
  140.  
    while ((line = in.readLine()) != null) {
  141.  
    result.append(line);
  142.  
    }
  143.  
    log.info("recv - {}" , result);
  144.  
    } catch (ConnectException e) {
  145.  
    log.error("调用HttpUtils.sendGet ConnectException, url=" url ",param=" param, e);
  146.  
    } catch (SocketTimeoutException e) {
  147.  
    log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" url ",param=" param, e);
  148.  
    } catch (IOException e) {
  149.  
    log.error("调用HttpUtils.sendGet IOException, url=" url ",param=" param, e);
  150.  
    } catch (Exception e) {
  151.  
    log.error("调用HttpsUtil.sendGet Exception, url=" url ",param=" param, e);
  152.  
    } finally {
  153.  
    try {
  154.  
    if (in != null) {
  155.  
    in.close();
  156.  
    }
  157.  
    } catch (Exception ex) {
  158.  
    log.error("调用in.close Exception, url=" url ",param=" param, ex);
  159.  
    }
  160.  
    }
  161.  
    return result.toString();
  162.  
    }
  163.  
     
  164.  
    private static boolean internalIp(String ip){
  165.  
    return internalIpList.contains(ip);
  166.  
    }
  167.  
    }
学新通

特别说明:这里我们将其解析封装成一个工具类,包含获取IP和ip地址解析两个方法,ip 的解析可以在请求中获取。获取到ip后,根据ip在xdb 中查找对应的IP地址的解析,由于是本地数据库可能存在一定的缺失,部分ip 存在无法解析的情况。 

ip2region v2.0 是一个离线 IP 地址定位库和 IP 定位数据管理框架,10 微秒级别的查询效率,准提供了众多主流编程语言的 xdb 数据生成和查询客户端实现。

数据聚合了一些知名 ip 到地名查询提供商的数据,这些是他们官方的的准确率,经测试着实比经典的纯真 IP 定位准确一些。

学新通

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

每个ip数据段的 region 信息都固定了格式:国家|区域|省份|城市|ISP,只有中国的数据绝大部分精确到了城市,其它国家部分数据只能定位到国家,后面的选项全部是0。

除了完全基于xdb文件的查询,我们还可以通过如下两种方式开启内存加速查询

第一种方式:缓存 VectorIndex 索引

我们可以提前从xdb文件中加载出来VectorIndex数据,然后全局缓存,每次创建Searcher对象的时候使用全局的VectorIndex缓存可以减少一次固定的IO操作,从而加速查询,减少IO压力。 

  1.  
    import org.lionsoul.ip2region.xdb.Searcher;
  2.  
     
  3.  
    public class Demo {
  4.  
     
  5.  
    public static void main(String[] args) {
  6.  
    // 1、从dbPath中预先加载VectorIndex索引,并且把这个得到的数据进行缓存作为全局变量,后续反复使用。
  7.  
    String dbPath = "文件路径";
  8.  
    byte[] vIndex =new byte[10];
  9.  
    try {
  10.  
    vIndex = Searcher.loadVectorIndexFromFile(dbPath);
  11.  
    } catch (Exception e) {
  12.  
    e.printStackTrace();
  13.  
    }
  14.  
     
  15.  
    // 2、使用全局的vIndex 创建带 VectorIndex 缓存的查询对象。
  16.  
    Searcher searcher;
  17.  
    try {
  18.  
    searcher = Searcher.newWithVectorIndex(dbPath, vIndex);
  19.  
    } catch (Exception e) {
  20.  
    e.printStackTrace();
  21.  
    }
  22.  
    }
  23.  
    }
学新通

第二种方式:缓存整个 xdb 文件数据

将整个xdb文件全部加载到内存,内存占用等同于xdb文件大小,无磁盘IO操作,保持微秒级别的查询效率。 

  1.  
    import org.lionsoul.ip2region.xdb.Searcher;
  2.  
     
  3.  
    public class Demo {
  4.  
     
  5.  
    public static void main(String[] args) {
  6.  
    // 1、根据dbPath直接加载整个xdb文件,并且把这个得到的数据进行缓存作为全局变量(存储到内存中)
  7.  
    String dbPath = "文件路径";
  8.  
    byte[] cBuff;
  9.  
    try {
  10.  
    cBuff = Searcher.loadContentFromFile(dbPath);
  11.  
    } catch (Exception e) {
  12.  
    e.printStackTrace();
  13.  
    return;
  14.  
    }
  15.  
     
  16.  
    // 2、使用上述的 cBuff 创建一个完全基于内存的查询对象
  17.  
    Searcher searcher;
  18.  
    try {
  19.  
    searcher = Searcher.newWithBuffer(cBuff);
  20.  
    } catch (Exception e) {
  21.  
    e.printStackTrace();
  22.  
    }
  23.  
    }
  24.  
    }
学新通

二、在线解析

如果想要获取更加全面的ip地址信息,可使用在线数据库,这里提供的是whois.pconline.com的IP解析,该IP解析在我的使用过程中表现非常流畅,而且只有少数的ip存在无法解析的情况。

特别说明:示例代码在上面

三、应用场景

那么在项目的什么流程获取ip地址是比较合适的,这里就要用到我们的拦截器了。拦截进入服务的每个请求,进行前置操作,对请求头的解析,获取ip以及ip属地。

  1.  
    import com.example.demo.utils.IPUtil;
  2.  
    import lombok.extern.slf4j.Slf4j;
  3.  
    import org.springframework.context.annotation.Configuration;
  4.  
    import org.springframework.web.servlet.HandlerInterceptor;
  5.  
    import org.springframework.web.servlet.ModelAndView;
  6.  
     
  7.  
    import javax.servlet.http.HttpServletRequest;
  8.  
    import javax.servlet.http.HttpServletResponse;
  9.  
     
  10.  
    @Slf4j
  11.  
    @Configuration
  12.  
    public class IpUrlLimitInterceptor implements HandlerInterceptor {
  13.  
     
  14.  
    @Override
  15.  
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {
  16.  
    /**
  17.  
    * 第一种方式:通过本地获取IP的具体地址
  18.  
    */
  19.  
    //String ip = IPUtil.getIp(httpServletRequest);
  20.  
    //String addr = IPUtil.getIpAddrByLocal(ip);
  21.  
    //String url = httpServletRequest.getRequestURI();
  22.  
     
  23.  
    /**
  24.  
    * 第二种方式: 通过在线库获取
  25.  
    */
  26.  
    String ip = IPUtil.getIp(httpServletRequest);
  27.  
    String addr = IPUtil.getIpAddrByByOnline(ip);
  28.  
    String url = httpServletRequest.getRequestURI();
  29.  
     
  30.  
    return true;
  31.  
    }
  32.  
     
  33.  
    @Override
  34.  
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
  35.  
     
  36.  
    }
  37.  
     
  38.  
    @Override
  39.  
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
  40.  
     
  41.  
    }
  42.  
    }
学新通
  1.  
    import org.springframework.beans.factory.annotation.Autowired;
  2.  
    import org.springframework.context.annotation.Configuration;
  3.  
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  4.  
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
  5.  
     
  6.  
    @Configuration
  7.  
    public class WebConfig extends WebMvcConfigurerAdapter {
  8.  
     
  9.  
    @Autowired
  10.  
    private IpUrlLimitInterceptor interceptor;
  11.  
     
  12.  
    @Override
  13.  
    public void addInterceptors(InterceptorRegistry registry) {
  14.  
    registry.addInterceptor(interceptor);
  15.  
    }
  16.  
    }
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhickcei
系列文章
更多 icon
同类精品
更多 icon
继续加载