您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

Spring Boot怎么快速实现IP地址解析

2024/4/15 6:56:25发布29次查看
引入:如果使用本地ip 解析的话,我们将会借助ip2region,该项目维护了一份较为详细的本地ip 地址对应表,如果为了离线环境的使用,需要导入该项目依赖,并指定版本,不同版本的方法可能存在差异。
<!-- ip库--><dependency> <groupid>org.lionsoul</groupid> <artifactid>ip2region</artifactid> <version>2.6.3</version> </dependency>
开发:在使用时需要将 xdb 文件下载到工程文件目录下,使用ip2region即使是完全基于 xdb 文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:
vindex 索引缓存 :使用固定的 512kib 的内存空间缓存 vector index 数据,减少一次 io 磁盘操作,保持平均查询效率稳定在10-20微秒之间。
xdb 整个文件缓存:将整个 xdb 文件全部加载到内存,内存占用等同于 xdb 文件大小,无磁盘 io 操作,保持微秒级别的查询效率。
/** * ip查询 */@slf4jpublic 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后,需要根据ip ,在xdb 中查找对应的ip地址的解析,由于是本地数据库可能存在一定的缺失,部分ip 存在无法解析的情况。
在线解析:如果想要获取更加全面的ip 地址信息,可使用在线数据库,这里提供的是 whois.pconline.com 的ip解析,该ip解析在我的使用过程中表现非常流畅,而且只有少数的ip 存在无法解析的情况。
@slf4jpublic 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 地址等信息。
/** * 对ip 进行限制,防止ip大量请求 */@slf4j@configurationpublic 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的视图层进行拦截才会触发我们的拦截器。
@configurationpublic class webconfig implements webmvcconfigurer { @autowired ipurllimitinterceptor ipurllimitinterceptor; //执行ip拦截器 @override public void addinterceptors(interceptorregistry registry){ registry.addinterceptor(ipurllimitinterceptor) // 拦截所有请求 .addpathpatterns("/**"); }}
通过这样的一套流程下来,我们就能实现对每一个请求进行ip 获取、ip解析,为每个请求带上具体ip地址的小尾巴。
以上就是spring boot怎么快速实现ip地址解析的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product