1.什么是ip2region?
ip2region v2.0 - 是一个离线ip地址定位库和ip定位数据管理框架,10微秒级别的查询效率,提供了众多主流编程语言的 xdb
数据生成和查询客户端实现。
ip2region 特性
1、标准化的数据格式
每个 ip 数据段的 region 信息都固定了格式:国家|区域|省份|城市|isp
,只有中国的数据绝大部分精确到了城市,其他国家部分数据只能定位到国家,后前的选项全部是0。
2、数据去重和压缩
xdb
格式生成程序会自动去重和压缩部分数据,默认的全部 ip 数据,生成的 ip2region.xdb 数据库是 11mib,随着数据的详细度增加数据库的大小也慢慢增大。
3、极速查询响应
即使是完全基于 xdb
文件的查询,单次查询响应时间在十微秒级别,可通过如下两种方式开启内存加速查询:
vindex
索引缓存 :使用固定的512kib
的内存空间缓存 vector index 数据,减少一次 io 磁盘操作,保持平均查询效率稳定在10-20微秒之间。xdb
整个文件缓存:将整个xdb
文件全部加载到内存,内存占用等同于xdb
文件大小,无磁盘 io 操作,保持微秒级别的查询效率。
4、ip 数据管理框架
v2.0 格式的 xdb
支持亿级别的 ip 数据段行数,region 信息也可以完全自定义,例如:你可以在 region 中追加特定业务需求的数据,例如:gps信息/国际统一地域信息编码/邮编等。也就是你完全可以使用 ip2region 来管理你自己的 ip 定位数据。
2.代码工程
实验目标
根据来源ip 判断属于那个国家,然后做过滤
pom.xml
<?xml version="1.0" encoding="utf-8"?> <project xmlns="http://maven.apache.org/pom/4.0.0" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://maven.apache.org/pom/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactid>springboot-demo</artifactid> <groupid>com.et</groupid> <version>1.0-snapshot</version> </parent> <modelversion>4.0.0</modelversion> <artifactid>ipfilter</artifactid> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-web</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-autoconfigure</artifactid> </dependency> <dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-test</artifactid> <scope>test</scope> </dependency> <!-- ip lib--> <dependency> <groupid>org.lionsoul</groupid> <artifactid>ip2region</artifactid> <version>2.6.4</version> </dependency> <dependency> <groupid>org.projectlombok</groupid> <artifactid>lombok</artifactid> </dependency> <dependency> <groupid>com.alibaba.fastjson2</groupid> <artifactid>fastjson2</artifactid> <version>2.0.40</version> </dependency> </dependencies> </project>
controller
package com.et.ipfilter.controller; import com.et.ipfilter.util.addressutils; import com.et.ipfilter.util.ipofflineutil; import com.et.ipfilter.util.iponlineutil; import lombok.extern.slf4j.slf4j; import org.springframework.util.stringutils; import org.springframework.web.bind.annotation.requestmapping; import org.springframework.web.bind.annotation.restcontroller; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; import java.util.hashmap; import java.util.map; @restcontroller @slf4j public class helloworldcontroller { @requestmapping("/hello") public map<string, object> showhelloworld(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse,string ip){ //fisrt get ip addr by offline file //string ip = ipofflineutil.getipaddr(httpservletrequest); //analyze address string addr = ipofflineutil.getaddr(ip); if(stringutils.isempty(addr)) { //get addr by online service ip = ipofflineutil.getipaddr(httpservletrequest); addr= addressutils.getrealaddressbyip(ip); log.info("ip >> {},address >> {}", ip, addr); // you can filter by country or province } map<string, object> map = new hashmap<>(); map.put("msg", "helloworld"); map.put("ipaddr", addr); return map; } }
dto
package com.et.ipfilter.dto; import lombok.allargsconstructor; import lombok.data; import lombok.noargsconstructor; @data @noargsconstructor @allargsconstructor public class countryinfo { private string query; private string status; private string country; private string countrycode; private string region; private string regionname; private string city; private string zip; private string lat; private string lon; private string timezone; private string isp; private string org; private string as; }
util
离线查询ip来源,
下载离线ip定位库
离线数据库在项目的data文件夹下,名称为ip2region.db
,其他2个文件是用于生成离线库的,可不用下载。
https://github.com/lionsoul2014/ip2region/tree/master/data/ip2region.xdb
下载到离线数据库后,我们需要读取这个数据库,我们可以放在项目的resources
目录
package com.et.ipfilter.util; import lombok.extern.slf4j.slf4j; import org.lionsoul.ip2region.xdb.searcher; import org.springframework.util.stringutils; import javax.servlet.http.httpservletrequest; /** * ip query */ @slf4j public class ipofflineutil { private static final string unknown = "unknown"; protected ipofflineutil() { } public static string getipaddr(httpservletrequest request) { string ip = request.getheader("x-forwarded-for"); if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("proxy-client-ip"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("wl-proxy-client-ip"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_x_forwarded_for"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_x_forwarded"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_x_cluster_client_ip"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_client_ip"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_forwarded_for"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_forwarded"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("http_via"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getheader("remote_addr"); } if (!stringutils.haslength(ip) || unknown.equalsignorecase(ip)) { ip = request.getremoteaddr(); } int index = ip.indexof(","); if (index != -1) { ip = ip.substring(0, index); } return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : ip; } public static string getaddr(string ip) { string dbpath = "d:\\ideaprojects\\etframework\\ipfilter\\src\\main\\resources\\ip2region\\ip2region.xdb"; // 1、from dbpath load all xdb to memory。 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、usr cbuff create a query object base on memory。 searcher searcher; try { searcher = searcher.newwithbuffer(cbuff); } catch (exception e) { log.info("failed to create content cached searcher: %s\n", e); return null; } // 3、query try { string region = searcher.search(ip); return region; } catch (exception e) { log.info("failed to search(%s): %s\n", ip, e); } return null; } }
在线查询访问ip
package com.et.ipfilter.util; import com.et.ipfilter.dto.countryinfo; import org.springframework.http.responseentity; import org.springframework.web.client.resttemplate; import javax.annotation.postconstruct; /** * @author liuhaihua * @version 1.0 * @classname iponlineutil * @description todo * @date 2024/08/09/ 9:58 */ public class iponlineutil { private static resttemplate resttemplate; private final resttemplate template; public static final string ip_api_url = "http://ip-api.com/json/"; public iponlineutil(resttemplate resttemplate) { this.template = resttemplate; } /** * init resttemplate */ @postconstruct public void init() { setresttemplate(this.template); } /** * init resttemplate */ private static void setresttemplate(resttemplate template) { resttemplate = template; } /** * get country by ip * * @param ip * @return */ public static countryinfo getcountrybyiponline(string ip) { responseentity<countryinfo> entity = resttemplate.getforentity( ip_api_url + ip + "?lang=zh-cn", countryinfo.class ); return entity.getbody(); } }
ip 查询工具类
package com.et.ipfilter.util; import org.lionsoul.ip2region.xdb.searcher; import java.io.ioexception; public class searcheriputils { public static string getcacheposition(string ip) { return searcheriputils.getcacheposition("src/main/resources/ip2region/ip2region.xdb", ip, true); } public static string getposition(string dbpath,string ip,boolean format) { // 1、create searcher object searcher searcher = null; try { searcher = searcher.newwithfileonly(dbpath); } catch (ioexception e) { throw new runtimeexception(e); } // 2、query try { string region = searcher.search(ip); if (format){ return region; } string[] split = region.split("\\|"); string s = split[0] + split[2] + split[3]; return s; } catch (exception e) { throw new runtimeexception(e); } } /** * @description : * @author : mabo */ public static string getindexcacheposition(string dbpath, string ip, boolean format) { searcher searcher = null; byte[] vindex; try { vindex = searcher.loadvectorindexfromfile(dbpath); } catch (exception e) { throw new runtimeexception(e); } try { searcher = searcher.newwithvectorindex(dbpath, vindex); } catch (exception e) { throw new runtimeexception(e); } try { string region = searcher.search(ip); if (format){ return region; } string[] split = region.split("\\|"); string s = split[0] + split[2] + split[3]; return s; } catch (exception e) { throw new runtimeexception(e); } } /** * @description : * @author : mabo */ public static string getcacheposition(string dbpath,string ip,boolean format) { byte[] cbuff; try { cbuff = searcher.loadcontentfromfile(dbpath); } catch (exception e) { throw new runtimeexception(e); } searcher searcher; try { searcher = searcher.newwithbuffer(cbuff); } catch (exception e) { throw new runtimeexception(e); } try { string region = searcher.search(ip); if (format){ return region; } string[] split = region.split("\\|"); string s = split[0] + split[2] + split[3]; return s; } catch (exception e) { throw new runtimeexception(e); } } } package com.et.ipfilter.util; import com.alibaba.fastjson2.jsonobject; import lombok.extern.slf4j.slf4j; import org.springframework.util.stringutils; import java.io.bufferedreader; import java.io.ioexception; import java.io.inputstreamreader; import java.net.connectexception; import java.net.sockettimeoutexception; import java.net.url; import java.net.urlconnection; @slf4j public class addressutils { 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 ip is inner ip,return if (internalip(ip)) { return "inner ip"; } if (true) { try { string rspstr = sendget(ip_url, "ip=" + ip + "&json=true" ,"gbk"); if (stringutils.isempty(rspstr)) { log.error("get addr exception {}" , 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("get addr exception {}" , 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("invoke httputils.sendget connectexception, url=" + url + ",param=" + param, e); } catch (sockettimeoutexception e) { log.error("invoke httputils.sendget sockettimeoutexception, url=" + url + ",param=" + param, e); } catch (ioexception e) { log.error("invoke httputils.sendget ioexception, url=" + url + ",param=" + param, e); } catch (exception e) { log.error("invoke httpsutil.sendget exception, url=" + url + ",param=" + param, e); } finally { try { if (in != null) { in.close(); } } catch (exception ex) { log.error("invoke in.close exception, url=" + url + ",param=" + param, ex); } } return result.tostring(); } /** * check the ip is inner? * * @param ip * @return */ public static boolean internalip(string ip) { byte[] addr = texttonumericformatv4(ip); return internalip(addr) || "127.0.0.1".equals(ip); } /** * check the ip is inner? * * @param addr * @return */ private static boolean internalip(byte[] addr) { if (null==addr|| addr.length < 2) { return true; } final byte b0 = addr[0]; final byte b1 = addr[1]; // 10.x.x.x/8 final byte section_1 = 0x0a; // 172.16.x.x/12 final byte section_2 = (byte) 0xac; final byte section_3 = (byte) 0x10; final byte section_4 = (byte) 0x1f; // 192.168.x.x/16 final byte section_5 = (byte) 0xc0; final byte section_6 = (byte) 0xa8; switch (b0) { case section_1: return true; case section_2: return (b1 >= section_3 && b1 <= section_4); case section_5: return (b1 == section_6); default: return false; } } /** * change ipv4 to byte * * @param text * @return byte */ private static byte[] texttonumericformatv4(string text) { if (text.length() == 0) { return new byte[0]; } byte[] bytes = new byte[4]; string[] elements = text.split("\\.", -1); try { long l; int i; switch (elements.length) { case 1: l = long.parselong(elements[0]); if ((l < 0l) || (l > 4294967295l)) { return new byte[0]; } bytes[0] = (byte) (int) (l >> 24 & 0xff); bytes[1] = (byte) (int) ((l & 0xffffff) >> 16 & 0xff); bytes[2] = (byte) (int) ((l & 0xffff) >> 8 & 0xff); bytes[3] = (byte) (int) (l & 0xff); break; case 2: l = integer.parseint(elements[0]); if ((l < 0l) || (l > 255l)) { return new byte[0]; } bytes[0] = (byte) (int) (l & 0xff); l = integer.parseint(elements[1]); if ((l < 0l) || (l > 16777215l)) { return new byte[0]; } bytes[1] = (byte) (int) (l >> 16 & 0xff); bytes[2] = (byte) (int) ((l & 0xffff) >> 8 & 0xff); bytes[3] = (byte) (int) (l & 0xff); break; case 3: for (i = 0; i < 2; ++i) { l = integer.parseint(elements[i]); if ((l < 0l) || (l > 255l)) { return new byte[0]; } bytes[i] = (byte) (int) (l & 0xff); } l = integer.parseint(elements[2]); if ((l < 0l) || (l > 65535l)) { return new byte[0]; } bytes[2] = (byte) (int) (l >> 8 & 0xff); bytes[3] = (byte) (int) (l & 0xff); break; case 4: for (i = 0; i < 4; ++i) { l = integer.parseint(elements[i]); if ((l < 0l) || (l > 255l)) { return new byte[0]; } bytes[i] = (byte) (int) (l & 0xff); } break; default: return new byte[0]; } } catch (numberformatexception e) { return new byte[0]; } return bytes; } }
以上只是一些关键代码,所有代码请参见下面代码仓库
代码仓库
3.测试
- 启动 springboot应用
- 访问http://127.0.0.1:8088/hello?ip=52.220.113.16
- 可以看到该ip属于新加坡,后续可以根据国家限制访问
以上就是springboot集成ip2region实现ip白名单的代码示例的详细内容,更多关于springboot ip白名单的资料请关注代码网其它相关文章!
发表评论