로컬 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!