SpringBoot quickly implements IP address resolution

If we use local ip analysis, we will use ip2region. This project maintains a more detailed local ip address correspondence table. If it is used in an offline environment, you need to import the project dependencies and specify the version. Different versions of the method may has a difference.

<dependency>
    <groupId>org.lionsoul</groupId>
    <artifactId>ip2region</artifactId>
    <version>2.6.3</version>
</dependency>

Official gitee: gitee.com/lionsoul/ip…

to develop

When using it, you need to download the xdb file to the project file directory. Even if you use ip2region to query completely based on the xdb file, the response time for a single query is at the level of ten microseconds. You can enable the memory acceleration query in the following two ways:

  • vIndex index cache: Use a fixed 512KiB memory space to cache vector index data, reduce one IO disk operation, and keep the average query efficiency stable between 10-20 microseconds.

  • xdb entire file cache: load the entire xdb file into the memory, the memory usage is equal to the size of the xdb file, no disk IO operations, and maintain query efficiency at the microsecond level.

/**
 * ip查询
 */
@Slf4j
public class IPUtil {

    private static final String UNKNOWN = "unknown";

    protected IPUtil(){ }

    /**
     * 获取 IP地址
     * 使用 Nginx等反向代理软件, 则不能通过 request.getRemoteAddr()获取 IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,
     * X-Forwarded-For中第一个非 unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip;
    }

    public static  String getAddr(String ip){
        String dbPath = "src/main/resources/ip2region/ip2region.xdb";
        // 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.searchByStr(ip);
            return region;
        } catch (Exception e) {
            log.info("failed to search(%s): %s\n", ip, e);
        }
        return null;
    }
}

Here we encapsulate ip analysis into a tool class, including two methods of obtaining IP and ip address analysis, and ip analysis can be obtained in the request. After obtaining the ip, you need to find the corresponding IP address analysis in xdb according to the ip. Because the local database may have certain deficiencies, some ips cannot be resolved.

online analysis

If you want to obtain more comprehensive ip address information, you can use an online database. Here is the IP analysis of whois.pconline.com. This IP analysis is very smooth during my use, and there are only a few ips that cannot be resolved. Case.

@Slf4j
public class AddressUtils {
    // IP地址查询
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";

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

    public static String getRealAddressByIP(String ip) {
        String address = UNKNOWN;
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        if (true) {
            try {
                String rspStr = sendGet(IP_URL, "ip=" + ip + "&json=true" ,"GBK");
                if (StrUtil.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();
    }
}

Scenes

So it is more appropriate to obtain the ip address in the process of development, and our interceptor will be used here. Intercept each request entering the service, perform pre-operations, and complete request header analysis, ip acquisition, and ip address analysis when entering, so that information such as ip addresses can be reused in the entire process of the subsequent process.

/**
 * 对ip 进行限制,防止IP大量请求
 */
@Slf4j
@Configuration
public class IpUrlLimitInterceptor implements HandlerInterceptor{

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) {

        //更新全局变量
        Constant.IP = IPUtil.getIpAddr(httpServletRequest);
        Constant.IP_ADDR = AddressUtils.getRealAddressByIP(Constant.IP);
        Constant.URL = httpServletRequest.getRequestURI();
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) {
        //通过本地获取
        //        获得ip
        //        String ip = IPUtil.getIpAddr(httpServletRequest);
        //解析具体地址
        //        String addr = IPUtil.getAddr(ip);

        //通过在线库获取
        //        String ip = IpUtils.getIpAddr(httpServletRequest);
        //        String ipaddr = AddressUtils.getRealAddressByIP(ipAddr);
        //        log.info("IP >> {},Address >> {}",ip,ipaddr);
    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

    }
}

If you want to execute our ip resolution interceptor,

Our interceptor needs to be intercepted at the view layer of spring boot.

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    IpUrlLimitInterceptor ipUrlLimitInterceptor;

    //执行ip拦截器
    @Override
    public void addInterceptors(InterceptorRegistry regivastry){
        registry.addInterceptor(ipUrlLimitInterceptor)
            // 拦截所有请求
            .addPathPatterns("/**");
    }
}

Guess you like

Origin blog.csdn.net/2301_78588786/article/details/131943584
Recommended