SpringBoot集成ip2region實現(xiàn)ip白名單的代碼示例
1.什么是ip2region?
ip2region v2.0 - 是一個離線IP地址定位庫和IP定位數(shù)據(jù)管理框架,10微秒級別的查詢效率,提供了眾多主流編程語言的 xdb 數(shù)據(jù)生成和查詢客戶端實現(xiàn)。
Ip2region 特性
1、標準化的數(shù)據(jù)格式
每個 ip 數(shù)據(jù)段的 region 信息都固定了格式:國家|區(qū)域|省份|城市|ISP,只有中國的數(shù)據(jù)絕大部分精確到了城市,其他國家部分數(shù)據(jù)只能定位到國家,后前的選項全部是0。
2、數(shù)據(jù)去重和壓縮
xdb 格式生成程序會自動去重和壓縮部分數(shù)據(jù),默認的全部 IP 數(shù)據(jù),生成的 ip2region.xdb 數(shù)據(jù)庫是 11MiB,隨著數(shù)據(jù)的詳細度增加數(shù)據(jù)庫的大小也慢慢增大。
3、極速查詢響應
即使是完全基于 xdb 文件的查詢,單次查詢響應時間在十微秒級別,可通過如下兩種方式開啟內(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è)務需求的數(shù)據(jù),例如:GPS信息/國際統(tǒng)一地域信息編碼/郵編等。也就是你完全可以使用 ip2region 來管理你自己的 IP 定位數(shù)據(jù)。
2.代碼工程
實驗目標
根據(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應用
- 訪問http://127.0.0.1:8088/hello?ip=52.220.113.16
- 可以看到該ip屬于新加坡,后續(xù)可以根據(jù)國家限制訪問
以上就是SpringBoot集成ip2region實現(xiàn)ip白名單的代碼示例的詳細內(nèi)容,更多關(guān)于SpringBoot ip白名單的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決Maven項目pom.xml導入了Junit包還是用不了@Test注解問題
在Maven項目中,如果在非test目錄下使用@Test注解,可能會因為pom.xml中<scope>test</scope>的設置而無法使用,正確做法是將測試代碼放在src/test/java目錄下,或去除<scope>test</scope>限制,這樣可以確保Junit依賴正確加載并應用于適當?shù)拇a部分2024-10-10
淺談Spring AOP中args()和argNames的含義
這篇文章主要介紹了Spring AOP中args()和argNames的含義,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
java數(shù)據(jù)結(jié)構(gòu)排序算法之樹形選擇排序詳解
這篇文章主要介紹了java數(shù)據(jù)結(jié)構(gòu)排序算法之樹形選擇排序,結(jié)合具體實例形式分析了java樹形選擇排序的原理、實現(xiàn)技巧與相關(guān)注意事項,需要的朋友可以參考下2017-05-05
聊聊注解@controller@service@component@repository的區(qū)別
這篇文章主要介紹了聊聊注解@controller@service@component@repository的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08

