SpringBoot中IP白名單控制實(shí)現(xiàn)限制接口訪問
一、IP白名單控制概述
1.1 什么是IP白名單
IP白名單(IP Whitelist)是一種網(wǎng)絡(luò)安全機(jī)制,它通過預(yù)先定義一組被允許訪問系統(tǒng)資源的IP地址列表,實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)請(qǐng)求來源的精確控制。只有來自白名單中IP地址的請(qǐng)求才會(huì)被系統(tǒng)接受和處理,其他所有來源的請(qǐng)求都將被拒絕。
在Spring Boot應(yīng)用中實(shí)現(xiàn)IP白名單控制具有以下核心價(jià)值:
- 增強(qiáng)安全性:有效防止未授權(quán)訪問,降低惡意攻擊風(fēng)險(xiǎn)
- 訪問控制:精確管理可訪問系統(tǒng)的客戶端范圍
- 資源保護(hù):避免非預(yù)期流量消耗服務(wù)器資源
- 合規(guī)要求:滿足某些行業(yè)對(duì)訪問控制的監(jiān)管要求
1.2 IP白名單 vs 黑名單
| 特性 | IP白名單 | IP黑名單 |
|---|---|---|
| 控制邏輯 | 默認(rèn)拒絕,明確允許 | 默認(rèn)允許,明確拒絕 |
| 安全性 | 更高,僅已知安全I(xiàn)P可訪問 | 較低,新型攻擊源可能不在名單中 |
| 維護(hù)成本 | 較高,需要持續(xù)更新合法IP | 較低,只需添加已知惡意IP |
| 適用場(chǎng)景 | 內(nèi)部系統(tǒng)、高安全要求接口 | 公開服務(wù)、需要阻止特定惡意源 |
| 誤殺可能性 | 可能誤拒絕合法但未登記的IP | 可能漏過未登記的惡意IP |
1.3 IP白名單的應(yīng)用場(chǎng)景
- 內(nèi)部管理系統(tǒng):限制只有公司內(nèi)網(wǎng)或VPN IP可以訪問
- 支付接口:確保只有支付網(wǎng)關(guān)的服務(wù)器IP可以調(diào)用
- 數(shù)據(jù)同步接口:僅允許合作伙伴的指定服務(wù)器IP訪問
- 管理后臺(tái):防止未授權(quán)的管理員訪問
- 第三方服務(wù)集成:限定合作方的調(diào)用來源
二、Spring Boot中實(shí)現(xiàn)IP白名單的技術(shù)選型
2.1 實(shí)現(xiàn)方案對(duì)比
在 Spring Boot 中實(shí)現(xiàn) IP 白名單功能時(shí),可以選擇多種技術(shù)方案。以下是常見的 技術(shù)選型 及其適用場(chǎng)景、優(yōu)缺點(diǎn)分析:
1. 自定義 Filter 實(shí)現(xiàn)
實(shí)現(xiàn)方式
- 通過實(shí)現(xiàn)
javax.servlet.Filter接口,在doFilter方法中檢查請(qǐng)求的 IP 是否在白名單中。 - 配合
@Component注解注冊(cè)為 Spring Bean,并配置攔截路徑。
優(yōu)點(diǎn)
- 簡單直接:無需依賴額外框架,代碼實(shí)現(xiàn)靈活。
- 低耦合:與 Spring Security 無關(guān),適合輕量級(jí)需求。
- 可擴(kuò)展性強(qiáng):支持動(dòng)態(tài)更新白名單(如從數(shù)據(jù)庫加載)。
缺點(diǎn)
- 需要手動(dòng)處理 IP 獲取邏輯:需注意代理服務(wù)器(如 Nginx)后的真實(shí) IP 獲取。
- 缺乏安全框架集成:無法與 Spring Security 的權(quán)限控制無縫結(jié)合。
適用場(chǎng)景
- 簡單項(xiàng)目或?qū)Π踩蟛桓叩膱?chǎng)景。
- 需要完全自定義邏輯(如動(dòng)態(tài)加載白名單)。
2. Spring Security 集成
實(shí)現(xiàn)方式
- 使用 Spring Security 提供的
hasIpAddress方法,配置白名單。 - 對(duì)于多 IP 白名單,需結(jié)合自定義表達(dá)式或策略。
優(yōu)點(diǎn)
- 與安全框架無縫集成:可與其他安全規(guī)則(如角色權(quán)限)結(jié)合。
- 配置靈活:支持基于路徑的細(xì)粒度控制。
缺點(diǎn)
- 配置復(fù)雜度較高:需要編寫自定義表達(dá)式或策略類。
- 不支持通配符:直接配置時(shí)僅支持精確匹配。
適用場(chǎng)景
- 已使用 Spring Security 的項(xiàng)目。
- 需要將 IP 白名單與其他安全規(guī)則(如 JWT、OAuth)結(jié)合。
3. AOP(面向切面編程)
實(shí)現(xiàn)方式
- 使用
@Aspect定義切面,在方法執(zhí)行前檢查 IP 是否在白名單中。 - 適用于對(duì)特定方法或類的訪問控制。
優(yōu)點(diǎn)
- 細(xì)粒度控制:可針對(duì)具體方法或類實(shí)現(xiàn) IP 攔截。
- 解耦業(yè)務(wù)邏輯:安全邏輯與業(yè)務(wù)代碼分離。
缺點(diǎn)
- 性能開銷:AOP 會(huì)增加方法調(diào)用的額外開銷。
- IP 獲取復(fù)雜:需在切面中獲取
HttpServletRequest對(duì)象。
適用場(chǎng)景
- 需要對(duì)特定方法(如敏感接口)進(jìn)行 IP 校驗(yàn)。
- 與業(yè)務(wù)邏輯解耦需求較高的場(chǎng)景。
4. 動(dòng)態(tài)白名單(結(jié)合數(shù)據(jù)庫)
實(shí)現(xiàn)方式
- 將白名單存儲(chǔ)在數(shù)據(jù)庫中,通過定時(shí)任務(wù)或緩存刷新機(jī)制動(dòng)態(tài)加載。
- 可結(jié)合 Redis 緩存提高性能。
優(yōu)點(diǎn)
- 靈活性高:無需重啟服務(wù)即可更新白名單。
- 適合生產(chǎn)環(huán)境:支持動(dòng)態(tài)管理 IP 列表。
缺點(diǎn)
- 實(shí)現(xiàn)復(fù)雜度較高:需處理數(shù)據(jù)庫連接、緩存更新等邏輯。
- 性能依賴數(shù)據(jù)庫:頻繁查詢數(shù)據(jù)庫可能影響性能。
適用場(chǎng)景
- 白名單需要頻繁更新的場(chǎng)景。
- 多租戶系統(tǒng)或需要?jiǎng)討B(tài)權(quán)限管理的系統(tǒng)。
5. 使用 Linux 防火墻(iptables/nftables)
實(shí)現(xiàn)方式
- 通過 Linux 命令行配置防火墻規(guī)則,限制訪問 IP。
- 與 Spring Boot 無關(guān),屬于操作系統(tǒng)層面的控制。
優(yōu)點(diǎn)
- 高性能:由操作系統(tǒng)直接處理,無需應(yīng)用層邏輯。
- 簡單高效:適合服務(wù)器級(jí)別的全局控制。
缺點(diǎn)
- 不可動(dòng)態(tài)更新:需手動(dòng)執(zhí)行命令或腳本。
- 缺乏靈活性:無法針對(duì)具體接口或方法控制。
適用場(chǎng)景
- 服務(wù)器級(jí)別的全局訪問控制。
- 與 Spring Boot 應(yīng)用無關(guān)的基礎(chǔ)設(shè)施防護(hù)。
6. 基于Interceptor的IP白名單
優(yōu)點(diǎn)
- 與 Spring MVC 集成良好:由于攔截器是 Spring MVC 的一部分,因此它能夠很好地集成到現(xiàn)有的 Spring Boot 應(yīng)用程序中。
- 適用于控制器級(jí)別的過濾:對(duì)于那些希望基于控制器或 API 級(jí)別進(jìn)行 IP 訪問控制的應(yīng)用來說,這是一個(gè)理想的選擇。
- 靈活性高:可以根據(jù)不同的 URL 模式應(yīng)用不同的攔截規(guī)則,使得可以在不同的場(chǎng)景下靈活運(yùn)用。
缺點(diǎn)
- 不適合全局過濾:對(duì)于不在控制器中的資源(例如靜態(tài)資源),可能無法有效攔截。如果需要對(duì)整個(gè)應(yīng)用程序?qū)嵤?IP 白名單策略,則可能不是最佳選擇。
- 性能開銷:雖然攔截器的性能開銷相對(duì)較小,但在高并發(fā)情況下,仍然需要注意其對(duì)系統(tǒng)性能的影響。
適用場(chǎng)景
- 需要對(duì)特定控制器或 API 進(jìn)行 IP 訪問控制的應(yīng)用:比如某些敏感的操作只能由特定的 IP 地址執(zhí)行,這時(shí)可以使用攔截器來限制訪問。
- 已構(gòu)建了復(fù)雜的 Spring MVC 應(yīng)用程序的項(xiàng)目:如果您的應(yīng)用程序已經(jīng)大量使用了 Spring MVC 的特性,那么使用攔截器將是一個(gè)自然且易于維護(hù)的選擇。
- 輕量級(jí)的 IP 白名單需求:當(dāng)您只需要簡單的 IP 白名單功能而不希望引入如 Spring Security 等較重的安全框架時(shí),攔截器提供了一個(gè)輕便的解決方案。
6. 技術(shù)選型對(duì)比表
| 技術(shù)方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
| 自定義 Filter | 簡單直接,靈活性高 | 需手動(dòng)處理 IP 獲取邏輯 | 輕量級(jí)項(xiàng)目或動(dòng)態(tài)白名單需求 |
| Spring Security | 與安全框架集成,配置靈活 | 配置復(fù)雜,不支持通配符 | 已使用 Spring Security 的項(xiàng)目 |
| AOP | 細(xì)粒度控制,解耦業(yè)務(wù)邏輯 | 性能開銷,IP 獲取復(fù)雜 | 特定方法或類的訪問控制 |
| 動(dòng)態(tài)白名單 | 靈活,適合生產(chǎn)環(huán)境 | 實(shí)現(xiàn)復(fù)雜,依賴數(shù)據(jù)庫性能 | 多租戶系統(tǒng)或動(dòng)態(tài)管理需求 |
| 基于 Interceptor | 與 Spring MVC 集成良好,適用于控制器層面的過濾 | 不適合全局過濾,如靜態(tài)資源等 | 控制器級(jí)別的 IP 訪問控制 |
| Linux 防火墻 | 高性能,簡單高效 | 不可動(dòng)態(tài)更新,缺乏靈活性 | 服務(wù)器級(jí)別全局控制 |
三、完整實(shí)現(xiàn)方案
3.1 自定義 Filter 實(shí)現(xiàn) IP 白名單
1.1 添加依賴
Spring Boot 默認(rèn)支持 Servlet API,無需額外依賴。
1.2 創(chuàng)建配置文件
在 application.yml 中配置白名單:
ip: whitelist: "192.168.1.1, 192.168.1.2"
1.3 創(chuàng)建 Filter 類
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* 自定義 IP 白名單 Filter
* 作用:攔截請(qǐng)求,檢查客戶端 IP 是否在白名單中
*/
@Component
public class IpWhiteListFilter implements Filter {
@Value("${ip.whitelist}")
private String whitelistStr;
// 用于存儲(chǔ)白名單的 List
private List<String> whiteList;
/**
* 初始化白名單
*/
@Override
public void init(FilterConfig filterConfig) {
whiteList = Arrays.stream(whitelistStr.split(","))
.map(String::trim)
.collect(java.util.stream.Collectors.toList());
}
/**
* 處理請(qǐng)求
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String clientIP = getClientIP(httpRequest); // 獲取真實(shí) IP
if (whiteList.contains(clientIP)) {
chain.doFilter(request, response); // IP 在白名單中,放行
} else {
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "IP 未授權(quán)訪問");
}
}
/**
* 獲取客戶端真實(shí) IP(支持代理服務(wù)器)
*/
private String getClientIP(HttpServletRequest request) {
// 從 X-Forwarded-For 頭獲取真實(shí) IP
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr(); // 如果沒有代理,直接獲取遠(yuǎn)程地址
}
return ip;
}
@Override
public void destroy() {
// 清理資源
}
}
1.4 注冊(cè) Filter(可選)
如果未使用 @Component 注解,可通過配置類注冊(cè):
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<IpWhiteListFilter> ipWhiteListFilter(IpWhiteListFilter filter) {
FilterRegistrationBean<IpWhiteListFilter> registration = new FilterRegistrationBean<>(filter);
registration.addUrlPatterns("/*"); // 攔截所有請(qǐng)求
return registration;
}
}
3.2 Spring Security 實(shí)現(xiàn) IP 白名單
2.1 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.2 配置白名單
在 application.yml 中配置白名單:
ip: whitelist: "192.168.1.1, 192.168.1.2"
2.3 創(chuàng)建 Security 配置類
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import java.util.Arrays;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${ip.whitelist}")
private String[] whiteList;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**")
.access("hasIpAddress('192.168.1.1') or hasIpAddress('192.168.1.2')") // 支持多個(gè) IP
.and()
.csrf().disable(); // 關(guān)閉 CSRF 保護(hù)
}
}
3.3 AOP 實(shí)現(xiàn) IP 白名單
3.1 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
3.2 創(chuàng)建自定義注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定義注解,標(biāo)記需要 IP 白名單保護(hù)的方法
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface IpWhiteList {
}
3.3 創(chuàng)建 AOP 切面類
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
/**
* AOP 切面類:檢查 IP 是否在白名單中
*/
@Aspect
@Component
public class IpWhiteListAspect {
@Value("${ip.whitelist}")
private String whitelistStr;
private List<String> whiteList;
public IpWhiteListAspect() {
// 初始化白名單
whiteList = Arrays.stream(whitelistStr.split(","))
.map(String::trim)
.collect(java.util.stream.Collectors.toList());
}
/**
* 切面邏輯:在方法執(zhí)行前檢查 IP
*/
@Around("@annotation(IpWhiteList)")
public Object checkIp(ProceedingJoinPoint joinPoint) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String clientIP = getClientIP(request);
if (whiteList.contains(clientIP)) {
return joinPoint.proceed(); // IP 在白名單中,繼續(xù)執(zhí)行方法
} else {
throw new AccessDeniedException("IP 未授權(quán)訪問");
}
}
/**
* 獲取客戶端真實(shí) IP(支持代理服務(wù)器)
*/
private String getClientIP(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}
}
3.4 在方法上使用注解
@RestController
public class DemoController {
@IpWhiteList
@GetMapping("/api/test")
public String test() {
return "Access granted";
}
}
3.4 動(dòng)態(tài)白名單(數(shù)據(jù)庫 + 緩存)
4.1 添加依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
4.2 創(chuàng)建數(shù)據(jù)庫表
CREATE TABLE sys_ip_whitelist (
id INT PRIMARY KEY AUTO_INCREMENT,
ip VARCHAR(45) NOT NULL
);
4.3 創(chuàng)建實(shí)體類
import jakarta.persistence.*;
@Entity
@Table(name = "sys_ip_whitelist")
public class SysIpWhitelist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String ip;
// Getters and Setters
}
4.4 創(chuàng)建 Mapper 接口
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysIpWhitelistRepository extends JpaRepository<SysIpWhitelist, Long> {
SysIpWhitelist findByIp(String ip);
}
4.5 創(chuàng)建 Service 類
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class WhiteListService {
@Autowired
private SysIpWhitelistRepository repository;
// 使用 Redis 緩存白名單(示例代碼)
@Autowired
private RedisTemplate<String, Object> redisTemplate;
private static final String WHITE_LIST_KEY = "ip:white_list";
/**
* 檢查 IP 是否在白名單中(支持緩存)
*/
public boolean isWhiteListed(String ip) {
// 先查緩存
List<String> cachedList = (List<String>) redisTemplate.opsForValue().get(WHITE_LIST_KEY);
if (cachedList != null && cachedList.contains(ip)) {
return true;
}
// 再查數(shù)據(jù)庫
SysIpWhitelist entity = repository.findByIp(ip);
if (entity != null) {
// 更新緩存
List<String> list = repository.findAll().stream().map(SysIpWhitelist::getIp).collect(Collectors.toList());
redisTemplate.opsForValue().set(WHITE_LIST_KEY, list, 1, TimeUnit.MINUTES);
return true;
}
return false;
}
}
4.6 在 Filter 或 AOP 中調(diào)用 Service
@Autowired
private WhiteListService whiteListService;
// 在 doFilter 或 checkIp 方法中調(diào)用
if (!whiteListService.isWhiteListed(clientIP)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "IP 未授權(quán)訪問");
}
3.5 Linux 防火墻實(shí)現(xiàn) IP 白名單
5.1 安裝 iptables
sudo apt update sudo apt install iptables
5.2 配置白名單規(guī)則
# 允許白名單 IP sudo iptables -A INPUT -s 192.168.1.1 -j ACCEPT sudo iptables -A INPUT -s 192.168.1.2 -j ACCEPT # 拒絕其他所有 IP sudo iptables -A INPUT -j DROP
5.3 保存規(guī)則(可選)
# 安裝保存工具 sudo apt install iptables-persistent # 保存規(guī)則 sudo netfilter-persistent save
3.6 基于Interceptor的IP白名單
1. 項(xiàng)目初始化與依賴配置
首先創(chuàng)建一個(gè)標(biāo)準(zhǔn)的Spring Boot項(xiàng)目,添加必要依賴:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok簡化代碼 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 配置處理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
2. IP白名單配置設(shè)計(jì)
創(chuàng)建配置類IpWhitelistProperties:
@ConfigurationProperties(prefix = "security.ip.whitelist")
@Data
public class IpWhitelistProperties {
/**
* 是否啟用IP白名單功能
*/
private boolean enabled = false;
/**
* 全局IP白名單列表
*/
private List<String> globalAllowedIps = new ArrayList<>();
/**
* 監(jiān)控模式:true-只記錄不攔截,false-實(shí)際攔截
*/
private boolean monitorMode = false;
/**
* 允許的IP段,支持CIDR表示法
*/
private List<String> allowedIpRanges = new ArrayList<>();
}
對(duì)應(yīng)的application.yml配置示例:
security:
ip:
whitelist:
enabled: true
global-allowed-ips:
- 192.168.1.100
- 172.16.0.50
allowed-ip-ranges:
- 10.0.0.0/8
- 192.168.0.0/16
monitor-mode: false
3. IP工具類實(shí)現(xiàn)
創(chuàng)建IpUtils工具類處理IP相關(guān)邏輯:
public class IpUtils {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST_IPV4 = "127.0.0.1";
private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
/**
* 獲取客戶端真實(shí)IP地址
*/
public static String getClientIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || UNKNOWN.equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
// 處理多級(jí)代理情況
if (ip != null && ip.contains(",")) {
ip = ip.substring(0, ip.indexOf(",")).trim();
}
return LOCALHOST_IPV6.equals(ip) ? LOCALHOST_IPV4 : ip;
}
/**
* 檢查IP是否匹配CIDR表示法的IP段
*/
public static boolean isIpInRange(String ip, String cidr) {
// 實(shí)現(xiàn)細(xì)節(jié)...
}
/**
* 驗(yàn)證IP地址格式
*/
public static boolean isValidIp(String ip) {
// 實(shí)現(xiàn)細(xì)節(jié)...
}
}
4. 自定義注解設(shè)計(jì)
創(chuàng)建@IpWhitelist注解支持方法級(jí)別的控制:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IpWhitelist {
/**
* 是否啟用IP白名單檢查
*/
boolean enabled() default true;
/**
* 特定于該接口的允許IP列表
*/
String[] allowedIps() default {};
/**
* 特定于該接口的允許IP段(CIDR)
*/
String[] allowedIpRanges() default {};
/**
* 拒絕時(shí)的錯(cuò)誤消息
*/
String message() default "Access denied by IP whitelist";
}
5. 攔截器核心實(shí)現(xiàn)
創(chuàng)建IpWhitelistInterceptor攔截器:
@RequiredArgsConstructor
public class IpWhitelistInterceptor implements HandlerInterceptor {
private final IpWhitelistProperties properties;
private final ObjectMapper objectMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!properties.isEnabled()) {
return true;
}
// 處理HandlerMethod情況
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 檢查類和方法上的注解
IpWhitelist classAnnotation = handlerMethod.getBeanType().getAnnotation(IpWhitelist.class);
IpWhitelist methodAnnotation = method.getAnnotation(IpWhitelist.class);
// 如果都沒有注解,則跳過檢查
if (classAnnotation == null && methodAnnotation == null) {
return true;
}
// 合并注解配置
boolean enabled = methodAnnotation != null ? methodAnnotation.enabled() :
classAnnotation.enabled();
String[] allowedIps = methodAnnotation != null && methodAnnotation.allowedIps().length > 0 ?
methodAnnotation.allowedIps() :
classAnnotation != null ? classAnnotation.allowedIps() : new String[0];
String[] allowedIpRanges = methodAnnotation != null && methodAnnotation.allowedIpRanges().length > 0 ?
methodAnnotation.allowedIpRanges() :
classAnnotation != null ? classAnnotation.allowedIpRanges() : new String[0];
String message = methodAnnotation != null ? methodAnnotation.message() :
classAnnotation != null ? classAnnotation.message() : "Access denied by IP whitelist";
if (!enabled) {
return true;
}
String clientIp = IpUtils.getClientIp(request);
// 檢查全局白名單
boolean isAllowed = properties.getGlobalAllowedIps().contains(clientIp) ||
properties.getAllowedIpRanges().stream()
.anyMatch(range -> IpUtils.isIpInRange(clientIp, range));
// 檢查注解指定的白名單
if (!isAllowed) {
isAllowed = Arrays.stream(allowedIps).anyMatch(ip -> ip.equals(clientIp)) ||
Arrays.stream(allowedIpRanges)
.anyMatch(range -> IpUtils.isIpInRange(clientIp, range));
}
if (!isAllowed) {
if (properties.isMonitorMode()) {
log.warn("IP whitelist monitor mode triggered. Denied IP: {}", clientIp);
return true;
}
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpStatus.FORBIDDEN.value());
response.getWriter().write(objectMapper.writeValueAsString(
Map.of(
"code", 403,
"message", message,
"data", null
)
));
return false;
}
return true;
}
}
6. 攔截器注冊(cè)配置
創(chuàng)建Web配置類注冊(cè)攔截器:
@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {
private final IpWhitelistProperties ipWhitelistProperties;
private final ObjectMapper objectMapper;
@Override
public void addInterceptors(InterceptorRegistry registry) {
if (ipWhitelistProperties.isEnabled()) {
registry.addInterceptor(new IpWhitelistInterceptor(ipWhitelistProperties, objectMapper))
.addPathPatterns("/**")
.order(Ordered.HIGHEST_PRECEDENCE); // 設(shè)置最高優(yōu)先級(jí)
}
}
}
7. 控制器示例
創(chuàng)建測(cè)試控制器驗(yàn)證功能:
@RestController
@RequestMapping("/api")
public class TestController {
@GetMapping("/public")
public String publicApi() {
return "This is a public API";
}
@IpWhitelist
@GetMapping("/secure")
public String secureApi() {
return "This is a secure API with IP whitelist";
}
@IpWhitelist(
allowedIps = {"192.168.1.100", "172.16.0.50"},
allowedIpRanges = {"10.0.0.0/8"},
message = "Custom IP restriction"
)
@GetMapping("/custom")
public String customApi() {
return "This API has custom IP whitelist rules";
}
}
3.7 注意事項(xiàng)
IP 獲取問題:
- 如果使用 Nginx 等反向代理,需從
X-Forwarded-For頭獲取真實(shí) IP。 - 示例代碼已處理代理邏輯。
- 如果使用 Nginx 等反向代理,需從
通配符支持:
- 如果需要支持 CIDR 格式(如
192.168.1.0/24),需自行實(shí)現(xiàn) CIDR 匹配邏輯。
- 如果需要支持 CIDR 格式(如
性能優(yōu)化:
- 動(dòng)態(tài)白名單建議使用緩存(如 Redis)減少數(shù)據(jù)庫查詢。
- 高并發(fā)場(chǎng)景下,F(xiàn)ilter 或 AOP 的性能開銷需評(píng)估。
安全性:
- 確保白名單邏輯不被繞過(如偽造
X-Forwarded-For頭)。 - 結(jié)合其他安全措施(如 HTTPS、JWT)增強(qiáng)防護(hù)。
- 確保白名單邏輯不被繞過(如偽造
四、進(jìn)階實(shí)現(xiàn):基于Spring Security的專業(yè)方案
4.1 Spring Security集成概述
對(duì)于企業(yè)級(jí)應(yīng)用,使用Spring Security可以提供更專業(yè)的安全控制。我們將基于前面的基礎(chǔ)實(shí)現(xiàn),重構(gòu)為Spring Security方案。
添加Spring Security依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
4.2 自定義AuthenticationFilter
創(chuàng)建IpAuthenticationFilter:
public class IpAuthenticationFilter extends OncePerRequestFilter {
private final IpWhitelistProperties properties;
private final ObjectMapper objectMapper;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 獲取請(qǐng)求路徑對(duì)應(yīng)的HandlerMethod
HandlerExecutionChain handlerExecutionChain = null;
try {
handlerExecutionChain = ((DispatcherServlet) request.getAttribute(DispatcherServlet.class.getName() + ".CONTEXT"))
.getHandler(request);
} catch (Exception e) {
filterChain.doFilter(request, response);
return;
}
if (handlerExecutionChain == null || !(handlerExecutionChain.getHandler() instanceof HandlerMethod)) {
filterChain.doFilter(request, response);
return;
}
HandlerMethod handlerMethod = (HandlerMethod) handlerExecutionChain.getHandler();
// 后續(xù)邏輯與Interceptor類似...
}
}
4.3 安全配置類
創(chuàng)建安全配置類:
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final IpWhitelistProperties ipWhitelistProperties;
private final ObjectMapper objectMapper;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.addFilterBefore(new IpAuthenticationFilter(ipWhitelistProperties, objectMapper),
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/public").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().disable()
.httpBasic().disable();
}
}
4.4 基于表達(dá)式的安全控制
Spring Security支持使用@PreAuthorize注解實(shí)現(xiàn)更靈活的控制:
@RestController
@RequestMapping("/api/v2")
public class SecureController {
@PreAuthorize("@ipSecurityService.isAllowed(#request)")
@GetMapping("/expression")
public String expressionBasedApi(HttpServletRequest request) {
return "This API uses expression-based IP control";
}
}
@Service
public class IpSecurityService {
public boolean isAllowed(HttpServletRequest request) {
// 實(shí)現(xiàn)IP檢查邏輯
return true;
}
}
五、生產(chǎn)級(jí)增強(qiáng)功能
5.1 動(dòng)態(tài)IP白名單管理
實(shí)現(xiàn)動(dòng)態(tài)更新白名單而不重啟應(yīng)用:
@Service
public class DynamicIpWhitelistService {
private final List<String> dynamicAllowedIps = new CopyOnWriteArrayList<>();
private final List<String> dynamicAllowedIpRanges = new CopyOnWriteArrayList<>();
public synchronized void addAllowedIp(String ip) {
if (IpUtils.isValidIp(ip) && !dynamicAllowedIps.contains(ip)) {
dynamicAllowedIps.add(ip);
}
}
public synchronized void removeAllowedIp(String ip) {
dynamicAllowedIps.remove(ip);
}
public synchronized void reloadIps(List<String> ips) {
dynamicAllowedIps.clear();
dynamicAllowedIps.addAll(ips.stream()
.filter(IpUtils::isValidIp)
.collect(Collectors.toList()));
}
// 類似方法實(shí)現(xiàn)IP段管理...
}
5.2 結(jié)合Redis的分布式IP白名單
對(duì)于分布式系統(tǒng),白名單需要集中存儲(chǔ):
@Configuration
public class RedisIpWhitelistConfig {
@Bean
public RedisTemplate<String, String> ipWhitelistRedisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, String> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new StringRedisSerializer());
return template;
}
}
@Service
@RequiredArgsConstructor
public class RedisIpWhitelistService {
private static final String WHITELIST_KEY = "ip:whitelist";
private static final String WHITELIST_RANGES_KEY = "ip:whitelist:ranges";
private final RedisTemplate<String, String> redisTemplate;
public boolean isAllowed(String ip) {
// 檢查精確IP
Boolean isMember = redisTemplate.opsForSet().isMember(WHITELIST_KEY, ip);
if (Boolean.TRUE.equals(isMember)) {
return true;
}
// 檢查IP段
Set<String> ranges = redisTemplate.opsForSet().members(WHITELIST_RANGES_KEY);
if (ranges != null) {
return ranges.stream().anyMatch(range -> IpUtils.isIpInRange(ip, range));
}
return false;
}
// 其他管理方法...
}
5.3 性能優(yōu)化與緩存
實(shí)現(xiàn)本地緩存減少遠(yuǎn)程調(diào)用:
@Service
public class CachedIpWhitelistService {
private final LoadingCache<String, Boolean> ipCheckCache;
public CachedIpWhitelistService(RedisIpWhitelistService redisService) {
this.ipCheckCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build(redisService::isAllowed);
}
public boolean isAllowed(String ip) {
return Boolean.TRUE.equals(ipCheckCache.get(ip));
}
}
六、測(cè)試策略與驗(yàn)證
6.1 單元測(cè)試
針對(duì)核心工具類編寫單元測(cè)試:
class IpUtilsTest {
@Test
void testIsIpInRange() {
assertTrue(IpUtils.isIpInRange("192.168.1.100", "192.168.1.0/24"));
assertFalse(IpUtils.isIpInRange("10.0.0.5", "192.168.1.0/24"));
}
@Test
void testGetClientIp() {
// 模擬HttpServletRequest測(cè)試各種頭部情況
}
}
6.2 集成測(cè)試
使用MockMvc測(cè)試完整流程:
@SpringBootTest
@AutoConfigureMockMvc
class IpWhitelistIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
@WithMockUser
void testPublicApi() throws Exception {
mockMvc.perform(get("/api/public"))
.andExpect(status().isOk());
}
@Test
@WithMockUser
void testSecureApiWithAllowedIp() throws Exception {
mockMvc.perform(get("/api/secure").with(request -> {
request.setRemoteAddr("192.168.1.100");
return request;
}))
.andExpect(status().isOk());
}
@Test
@WithMockUser
void testSecureApiWithDeniedIp() throws Exception {
mockMvc.perform(get("/api/secure").with(request -> {
request.setRemoteAddr("10.1.2.3");
return request;
}))
.andExpect(status().isForbidden());
}
}
6.3 性能測(cè)試
使用JMeter模擬不同負(fù)載下的表現(xiàn):

七、生產(chǎn)環(huán)境最佳實(shí)踐
7.1 安全建議
- 最小權(quán)限原則:只添加必要的IP到白名單
- 定期審計(jì):每月審查白名單,移除不再需要的IP
- 日志記錄:詳細(xì)記錄所有被拒絕的訪問嘗試
- 監(jiān)控告警:對(duì)異常訪問模式設(shè)置告警
- 備份機(jī)制:定期備份白名單配置
7.2 性能調(diào)優(yōu)
- 緩存策略:對(duì)IP檢查結(jié)果進(jìn)行適當(dāng)緩存
- 異步檢查:對(duì)于非關(guān)鍵路徑可以考慮異步驗(yàn)證
- CIDR優(yōu)化:將IP段檢查算法優(yōu)化為O(1)復(fù)雜度
- 并發(fā)控制:使用線程安全的數(shù)據(jù)結(jié)構(gòu)
- 預(yù)熱機(jī)制:應(yīng)用啟動(dòng)時(shí)預(yù)加載常用IP
7.3 故障排查指南
常見問題及解決方案:
| 問題現(xiàn)象 | 可能原因 | 解決方案 |
|---|---|---|
| 合法IP被拒絕 | IP獲取不正確 | 檢查X-Forwarded-For等頭部配置 |
| 白名單修改后不生效 | 緩存未刷新 | 實(shí)現(xiàn)配置變更通知機(jī)制 |
| 性能下降 | IP檢查成為瓶頸 | 引入緩存、優(yōu)化算法或水平擴(kuò)展 |
| 分布式環(huán)境不一致 | 各節(jié)點(diǎn)白名單不同步 | 使用集中式存儲(chǔ)如Redis |
| IPv6支持問題 | 只配置了IPv4 | 確保同時(shí)支持IPv4和IPv6格式 |
八、擴(kuò)展與演進(jìn)
8.1 結(jié)合微服務(wù)架構(gòu)
在Spring Cloud體系中,可以在網(wǎng)關(guān)層統(tǒng)一實(shí)現(xiàn)IP白名單:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("secure-service", r -> r.path("/api/secure/**")
.filters(f -> f.filter(new IpWhitelistGatewayFilter()))
.uri("lb://secure-service"))
.build();
}
8.2 Kubernetes環(huán)境適配
在K8s中需要考慮Pod IP動(dòng)態(tài)變化的問題:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-whitelist
spec:
podSelector:
matchLabels:
app: spring-boot-app
ingress:
- from:
- ipBlock:
cidr: 192.168.1.0/24
ports:
- protocol: TCP
port: 8080
8.3 未來演進(jìn)方向
- 機(jī)器學(xué)習(xí)分析:自動(dòng)識(shí)別異常IP并動(dòng)態(tài)調(diào)整白名單
- 區(qū)塊鏈存證:將白名單變更記錄上鏈確保不可篡改
- 零信任整合:與零信任架構(gòu)深度集成
- IoT擴(kuò)展:支持設(shè)備指紋等更多維度的認(rèn)證
- 量子安全:為后量子時(shí)代提前準(zhǔn)備加密方案
九、總結(jié)與展望
本文詳細(xì)探討了在Spring Boot應(yīng)用中實(shí)現(xiàn)IP白名單控制的多種方案,從基礎(chǔ)的Interceptor實(shí)現(xiàn)到基于Spring Security的專業(yè)方案,再到生產(chǎn)級(jí)的增強(qiáng)功能和分布式環(huán)境適配。通過本教程,您應(yīng)該能夠:
- 深入理解IP白名單的原理和價(jià)值
- 掌握在Spring Boot中實(shí)現(xiàn)IP控制的多種技術(shù)
- 具備構(gòu)建生產(chǎn)級(jí)IP白名單系統(tǒng)的能力
- 了解相關(guān)的最佳實(shí)踐和優(yōu)化策略
IP白名單作為網(wǎng)絡(luò)安全的基礎(chǔ)設(shè)施之一,在云原生和微服務(wù)架構(gòu)不斷演進(jìn)的今天,仍然發(fā)揮著不可替代的作用。未來,隨著邊緣計(jì)算和5G技術(shù)的普及,IP白名單技術(shù)可能會(huì)與更多新興技術(shù)融合,形成更智能、更靈活的訪問控制體系。
到此這篇關(guān)于SpringBoot中IP白名單控制實(shí)現(xiàn)限制接口訪問的文章就介紹到這了,更多相關(guān)SpringBoot IP白名單 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java面試突擊為什么要用HTTPS及它的優(yōu)點(diǎn)
這篇文章主要介紹了Java面試突擊為什么要用HTTPS及它的優(yōu)點(diǎn),文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-07-07
SpringBoot3整合Nacos?V2.3.2的詳細(xì)過程
本文介紹了如何在?Spring?Boot?3.2.x?項(xiàng)目中整合?Nacos?2.3.2,包括依賴配置、Nacos?服務(wù)發(fā)現(xiàn)與動(dòng)態(tài)配置的配置方法,通過整合?Nacos,Spring?Boot?應(yīng)用可以實(shí)現(xiàn)高效的服務(wù)發(fā)現(xiàn)、動(dòng)態(tài)配置管理以及分布式系統(tǒng)中的靈活擴(kuò)展,感興趣的朋友跟隨小編一起看看吧2024-11-11
springboot中獲取配置文件中屬性值的幾種方式小結(jié)
本文主要介紹了springboot中獲取配置文件中屬性值的幾種方式小結(jié),主要介紹了六種方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
Maven配置項(xiàng)目依賴使用本地倉庫的方法匯總(小結(jié))
這篇文章主要介紹了Maven配置項(xiàng)目依賴使用本地倉庫的方法匯總(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
Java實(shí)現(xiàn)線程按序交替執(zhí)行的方法詳解
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)線程按序交替執(zhí)行,文中的示例代碼講解詳細(xì),對(duì)我們了解線程有一定幫助,需要的可以參考一下2022-10-10
詳解Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性
Chrome 51 開始,瀏覽器的 Cookie 新增加了一個(gè)SameSite屬性,用來防止 CSRF 攻擊和用戶追蹤。今天通過本文給大家介紹Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性,感興趣的朋友一起看看吧2022-01-01

