SpringBoot集成ip2region實現(xiàn)ip白名單的代碼示例
1.什么是ip2region?
ip2region v2.0 - 是一個離線IP地址定位庫和IP定位數(shù)據(jù)管理框架,10微秒級別的查詢效率,提供了眾多主流編程語言的 xdb
數(shù)據(jù)生成和查詢客戶端實現(xiàn)。
Ip2region 特性
1、標(biāo)準(zhǔn)化的數(shù)據(jù)格式
每個 ip 數(shù)據(jù)段的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP
,只有中國的數(shù)據(jù)絕大部分精確到了城市,其他國家部分?jǐn)?shù)據(jù)只能定位到國家,后前的選項全部是0。
2、數(shù)據(jù)去重和壓縮
xdb
格式生成程序會自動去重和壓縮部分?jǐn)?shù)據(jù),默認(rèn)的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細(xì)度增加數(shù)據(jù)庫的大小也慢慢增大。
3、極速查詢響應(yīng)
即使是完全基于 xdb
文件的查詢,單次查詢響應(yīng)時間在十微秒級別,可通過如下兩種方式開啟內(nèi)存加速查詢:
vIndex
索引緩存 :使用固定的512KiB
的內(nèi)存空間緩存 vector index 數(shù)據(jù),減少一次 IO 磁盤操作,保持平均查詢效率穩(wěn)定在10-20微秒之間。xdb
整個文件緩存:將整個xdb
文件全部加載到內(nèi)存,內(nèi)存占用等同于xdb
文件大小,無磁盤 IO 操作,保持微秒級別的查詢效率。
4、IP 數(shù)據(jù)管理框架
v2.0 格式的 xdb
支持億級別的 IP 數(shù)據(jù)段行數(shù),region 信息也可以完全自定義,例如:你可以在 region 中追加特定業(yè)務(wù)需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
2.代碼工程
實驗?zāi)繕?biāo)
根據(jù)來源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定位庫
離線數(shù)據(jù)庫在項目的data文件夾下,名稱為ip2region.db
,其他2個文件是用于生成離線庫的,可不用下載。
https://github.com/lionsoul2014/ip2region/tree/master/data/ip2region.xdb
下載到離線數(shù)據(jù)庫后,我們需要讀取這個數(shù)據(jù)庫,我們可以放在項目的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; } }
以上只是一些關(guān)鍵代碼,所有代碼請參見下面代碼倉庫
代碼倉庫
3.測試
- 啟動 springBoot應(yīng)用
- 訪問http://127.0.0.1:8088/hello?ip=52.220.113.16
- 可以看到該ip屬于新加坡,后續(xù)可以根據(jù)國家限制訪問
以上就是SpringBoot集成ip2region實現(xiàn)ip白名單的代碼示例的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot ip白名單的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring中propagation的7種事務(wù)配置及說明
這篇文章主要介紹了Spring中propagation的7種事務(wù)配置及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06Activiti explorer.war示例工程使用過程圖解
這篇文章主要介紹了Activiti explorer.war示例工程使用過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-03-03將JSON字符串?dāng)?shù)組轉(zhuǎn)對象集合方法步驟
這篇文章主要給大家介紹了關(guān)于將JSON字符串?dāng)?shù)組轉(zhuǎn)對象集合的方法步驟,文中通過代碼示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08關(guān)于Hadoop中Spark?Streaming的基本概念
這篇文章主要介紹了關(guān)于Hadoop中Spark?Streaming的基本概念,Spark?Streaming是構(gòu)建在Spark上的實時計算框架,它擴展了Spark處理大規(guī)模流式數(shù)據(jù)的能力,Spark?Streaming可結(jié)合批處理和交互式查詢,需要的朋友可以參考下2023-07-07spring使用WebSocket注入service層失敗問題及解決
這篇文章主要介紹了spring使用WebSocket注入service層失敗問題及解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07Java并發(fā)編程ArrayBlockingQueue的使用
ArrayBlockingQueue是一個備受矚目的有界阻塞隊列,本文將全面深入地介紹ArrayBlockingQueue的內(nèi)部機制、使用場景以及最佳實踐,感興趣的可以了解一下2024-08-08聊聊SpringBoot的@Scheduled的并發(fā)問題
這篇文章主要介紹了聊聊SpringBoot的@Scheduled的并發(fā)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11分頁技術(shù)原理與實現(xiàn)之Java+Oracle代碼實現(xiàn)分頁(二)
這篇文章主要介紹了分頁技術(shù)原理與實現(xiàn)的第二篇:Java+Oracle代碼實現(xiàn)分頁,感興趣的小伙伴們可以參考一下2016-06-06