Home  >  Article  >  Java  >  How Spring Boot quickly implements IP address resolution

How Spring Boot quickly implements IP address resolution

PHPz
PHPzforward
2023-05-10 15:04:161304browse

Introduction:

If you use local ip resolution, we will use ip2region. This project maintains a more detailed local ip address correspondence table. If you want to use it in an offline environment, you need to import this project. Depend on, and specify the version, the methods of different versions may be different.

<!--    ip库-->
<dependency>
	<groupId>org.lionsoul</groupId>
	<artifactId>ip2region</artifactId>
	<version>2.6.3</version>
        </dependency>

Development:

You need to download the xdb file to the project file directory when using it. Even if you use ip2region to query completely based on the xdb file, the response time of a single query is at the level of ten microseconds. , memory accelerated query can be enabled 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 maintain average query efficiency Stable between 10-20 microseconds.

  • xdb entire file cache: Load the entire xdb file into memory. The memory usage is equal to the size of the xdb file. There is no disk IO operation and microsecond-level query efficiency is maintained.

/**
 * 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 resolution into a tool class, including two methods of obtaining IP and ip address resolution. The ip resolution can be obtained in the request. After obtaining the IP, you need to find the resolution of the corresponding IP address in xdb based on the IP. Since 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 the online database. What is provided here is the IP analysis of whois.pconline.com. The IP analysis is in my The performance is very smooth during use, and only a few IPs cannot be resolved.

@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();
    }
}

Scenario:

So in what process of development is it more appropriate to obtain the IP address? Our interceptor will be used here. Intercept every request that enters the service, perform pre-operations, and complete the parsing of the request header, IP acquisition, and IP address resolution when entering, so that the IP address and other information can be reused in all subsequent processes.

/**
 * 对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) {

    }
}
如果想要执行我们的ip 解析拦截器,需要在spring boot的视图层进行拦截才会触发我们的拦截器。
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    IpUrlLimitInterceptor ipUrlLimitInterceptor;
	
	    //执行ip拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(ipUrlLimitInterceptor)
                // 拦截所有请求
                .addPathPatterns("/**");
    }
}
通过这样的一套流程下来,我们就能实现对每一个请求进行ip 获取、ip解析,为每个请求带上具体ip地址的小尾巴。

The above is the detailed content of How Spring Boot quickly implements IP address resolution. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete