>  기사  >  Java  >  Spring Boot가 IP 주소 확인을 신속하게 구현하는 방법

Spring Boot가 IP 주소 확인을 신속하게 구현하는 방법

PHPz
PHPz앞으로
2023-05-10 15:04:161305검색

소개:

로컬 IP 해상도를 사용하는 경우 ip2region을 사용합니다. 이 프로젝트는 보다 자세한 로컬 IP 주소 대응 테이블을 유지 관리합니다. 오프라인 환경에서 사용하려면 프로젝트 종속성을 가져와서 지정해야 합니다. 버전마다 방법이 다를 수 있습니다.

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

개발:

사용시 xdb 파일을 프로젝트 파일 디렉터리에 다운로드해야 합니다. ip2region을 사용하여 xdb 파일을 기반으로 완전히 쿼리하더라도 단일 쿼리의 응답 시간은 10 수준입니다. 다음 두 가지 방법으로 열 수 있습니다. 메모리 가속 쿼리:

  • vIndex 인덱스 캐시: 고정 512KiB 메모리 공간을 사용하여 벡터 인덱스 데이터를 캐시하고 1개의 IO 디스크 작업을 줄이며 평균 쿼리 효율성을 안정적으로 유지합니다. 10~20마이크로초.

  • xdb 전체 파일 캐시: 전체 xdb 파일을 메모리에 로드합니다. 메모리 공간은 xdb 파일 크기와 동일하며 마이크로초 수준의 쿼리 효율성을 유지하는 디스크 IO 작업이 필요하지 않습니다.

/**
 * 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;
    }

여기에서는 IP를 얻는 두 가지 방법과 IP 주소 확인을 포함하여 IP 확인을 도구 클래스로 캡슐화합니다. IP를 얻은 후에는 해당 IP를 기반으로 xdb에서 해당 IP 주소의 해상도를 찾아야 합니다. 로컬 데이터베이스에는 특정 결함이 있을 수 있으므로 일부 IP는 확인할 수 없습니다.

온라인 분석:

더 포괄적인 IP 주소 정보를 얻으려면 온라인 데이터베이스를 사용할 수 있습니다. 여기서 제공되는 것은 whois.pconline.com의 IP 분석입니다. 이 IP 분석은 제가 사용하는 동안 매우 원활하게 수행되었습니다. 일부 IP만 확인할 수 없습니다.

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

시나리오:

그러면 어떤 개발 과정에서 IP 주소를 얻는 것이 더 적절합니까? 여기서 인터셉터가 사용됩니다. 서비스에 들어오는 모든 요청을 차단하고 사전 작업을 수행하며, 진입 시 요청 헤더 파싱, IP 획득, IP 주소 확인을 완료하여 IP 주소 등의 정보가 모든 후속 프로세스에서 재사용될 수 있도록 합니다.

rreee
/**
 * 对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("/**");
    }
}

위 내용은 Spring Boot가 IP 주소 확인을 신속하게 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제