SpringBoot中IP白名單控制實(shí)現(xiàn)限制接口訪問(wèn)
一、IP白名單控制概述
1.1 什么是IP白名單
IP白名單(IP Whitelist)是一種網(wǎng)絡(luò)安全機(jī)制,它通過(guò)預(yù)先定義一組被允許訪問(wèn)系統(tǒng)資源的IP地址列表,實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)請(qǐng)求來(lái)源的精確控制。只有來(lái)自白名單中IP地址的請(qǐng)求才會(huì)被系統(tǒng)接受和處理,其他所有來(lái)源的請(qǐng)求都將被拒絕。
在Spring Boot應(yīng)用中實(shí)現(xiàn)IP白名單控制具有以下核心價(jià)值:
- 增強(qiáng)安全性:有效防止未授權(quán)訪問(wèn),降低惡意攻擊風(fēng)險(xiǎn)
- 訪問(wèn)控制:精確管理可訪問(wèn)系統(tǒng)的客戶端范圍
- 資源保護(hù):避免非預(yù)期流量消耗服務(wù)器資源
- 合規(guī)要求:滿足某些行業(yè)對(duì)訪問(wèn)控制的監(jiān)管要求
1.2 IP白名單 vs 黑名單
特性 | IP白名單 | IP黑名單 |
---|---|---|
控制邏輯 | 默認(rèn)拒絕,明確允許 | 默認(rèn)允許,明確拒絕 |
安全性 | 更高,僅已知安全I(xiàn)P可訪問(wèn) | 較低,新型攻擊源可能不在名單中 |
維護(hù)成本 | 較高,需要持續(xù)更新合法IP | 較低,只需添加已知惡意IP |
適用場(chǎng)景 | 內(nèi)部系統(tǒng)、高安全要求接口 | 公開服務(wù)、需要阻止特定惡意源 |
誤殺可能性 | 可能誤拒絕合法但未登記的IP | 可能漏過(guò)未登記的惡意IP |
1.3 IP白名單的應(yīng)用場(chǎng)景
- 內(nèi)部管理系統(tǒng):限制只有公司內(nèi)網(wǎng)或VPN IP可以訪問(wèn)
- 支付接口:確保只有支付網(wǎng)關(guān)的服務(wù)器IP可以調(diào)用
- 數(shù)據(jù)同步接口:僅允許合作伙伴的指定服務(wù)器IP訪問(wèn)
- 管理后臺(tái):防止未授權(quán)的管理員訪問(wèn)
- 第三方服務(wù)集成:限定合作方的調(diào)用來(lái)源
二、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)方式
- 通過(guò)實(shí)現(xiàn)
javax.servlet.Filter
接口,在doFilter
方法中檢查請(qǐng)求的 IP 是否在白名單中。 - 配合
@Component
注解注冊(cè)為 Spring Bean,并配置攔截路徑。
優(yōu)點(diǎn)
- 簡(jiǎn)單直接:無(wú)需依賴額外框架,代碼實(shí)現(xiàn)靈活。
- 低耦合:與 Spring Security 無(wú)關(guān),適合輕量級(jí)需求。
- 可擴(kuò)展性強(qiáng):支持動(dòng)態(tài)更新白名單(如從數(shù)據(jù)庫(kù)加載)。
缺點(diǎn)
- 需要手動(dòng)處理 IP 獲取邏輯:需注意代理服務(wù)器(如 Nginx)后的真實(shí) IP 獲取。
- 缺乏安全框架集成:無(wú)法與 Spring Security 的權(quán)限控制無(wú)縫結(jié)合。
適用場(chǎng)景
- 簡(jiǎn)單項(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)
- 與安全框架無(wú)縫集成:可與其他安全規(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ì)特定方法或類的訪問(wèn)控制。
優(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ù)庫(kù))
實(shí)現(xiàn)方式
- 將白名單存儲(chǔ)在數(shù)據(jù)庫(kù)中,通過(guò)定時(shí)任務(wù)或緩存刷新機(jī)制動(dòng)態(tài)加載。
- 可結(jié)合 Redis 緩存提高性能。
優(yōu)點(diǎn)
- 靈活性高:無(wú)需重啟服務(wù)即可更新白名單。
- 適合生產(chǎn)環(huán)境:支持動(dòng)態(tài)管理 IP 列表。
缺點(diǎn)
- 實(shí)現(xiàn)復(fù)雜度較高:需處理數(shù)據(jù)庫(kù)連接、緩存更新等邏輯。
- 性能依賴數(shù)據(jù)庫(kù):頻繁查詢數(shù)據(jù)庫(kù)可能影響性能。
適用場(chǎng)景
- 白名單需要頻繁更新的場(chǎng)景。
- 多租戶系統(tǒng)或需要?jiǎng)討B(tài)權(quán)限管理的系統(tǒng)。
5. 使用 Linux 防火墻(iptables/nftables)
實(shí)現(xiàn)方式
- 通過(guò) Linux 命令行配置防火墻規(guī)則,限制訪問(wèn) IP。
- 與 Spring Boot 無(wú)關(guān),屬于操作系統(tǒng)層面的控制。
優(yōu)點(diǎn)
- 高性能:由操作系統(tǒng)直接處理,無(wú)需應(yīng)用層邏輯。
- 簡(jiǎn)單高效:適合服務(wù)器級(jí)別的全局控制。
缺點(diǎn)
- 不可動(dòng)態(tài)更新:需手動(dòng)執(zhí)行命令或腳本。
- 缺乏靈活性:無(wú)法針對(duì)具體接口或方法控制。
適用場(chǎng)景
- 服務(wù)器級(jí)別的全局訪問(wèn)控制。
- 與 Spring Boot 應(yīng)用無(wú)關(guān)的基礎(chǔ)設(shè)施防護(hù)。
6. 基于Interceptor的IP白名單
優(yōu)點(diǎn)
- 與 Spring MVC 集成良好:由于攔截器是 Spring MVC 的一部分,因此它能夠很好地集成到現(xiàn)有的 Spring Boot 應(yīng)用程序中。
- 適用于控制器級(jí)別的過(guò)濾:對(duì)于那些希望基于控制器或 API 級(jí)別進(jìn)行 IP 訪問(wèn)控制的應(yīng)用來(lái)說(shuō),這是一個(gè)理想的選擇。
- 靈活性高:可以根據(jù)不同的 URL 模式應(yīng)用不同的攔截規(guī)則,使得可以在不同的場(chǎng)景下靈活運(yùn)用。
缺點(diǎn)
- 不適合全局過(guò)濾:對(duì)于不在控制器中的資源(例如靜態(tài)資源),可能無(wú)法有效攔截。如果需要對(duì)整個(gè)應(yīng)用程序?qū)嵤?IP 白名單策略,則可能不是最佳選擇。
- 性能開銷:雖然攔截器的性能開銷相對(duì)較小,但在高并發(fā)情況下,仍然需要注意其對(duì)系統(tǒng)性能的影響。
適用場(chǎng)景
- 需要對(duì)特定控制器或 API 進(jìn)行 IP 訪問(wèn)控制的應(yīng)用:比如某些敏感的操作只能由特定的 IP 地址執(zhí)行,這時(shí)可以使用攔截器來(lái)限制訪問(wèn)。
- 已構(gòu)建了復(fù)雜的 Spring MVC 應(yīng)用程序的項(xiàng)目:如果您的應(yīng)用程序已經(jīng)大量使用了 Spring MVC 的特性,那么使用攔截器將是一個(gè)自然且易于維護(hù)的選擇。
- 輕量級(jí)的 IP 白名單需求:當(dāng)您只需要簡(jiǎn)單的 IP 白名單功能而不希望引入如 Spring Security 等較重的安全框架時(shí),攔截器提供了一個(gè)輕便的解決方案。
6. 技術(shù)選型對(duì)比表
技術(shù)方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
---|---|---|---|
自定義 Filter | 簡(jiǎn)單直接,靈活性高 | 需手動(dòng)處理 IP 獲取邏輯 | 輕量級(jí)項(xiàng)目或動(dòng)態(tài)白名單需求 |
Spring Security | 與安全框架集成,配置靈活 | 配置復(fù)雜,不支持通配符 | 已使用 Spring Security 的項(xiàng)目 |
AOP | 細(xì)粒度控制,解耦業(yè)務(wù)邏輯 | 性能開銷,IP 獲取復(fù)雜 | 特定方法或類的訪問(wèn)控制 |
動(dòng)態(tài)白名單 | 靈活,適合生產(chǎn)環(huán)境 | 實(shí)現(xiàn)復(fù)雜,依賴數(shù)據(jù)庫(kù)性能 | 多租戶系統(tǒng)或動(dòng)態(tài)管理需求 |
基于 Interceptor | 與 Spring MVC 集成良好,適用于控制器層面的過(guò)濾 | 不適合全局過(guò)濾,如靜態(tài)資源等 | 控制器級(jí)別的 IP 訪問(wèn)控制 |
Linux 防火墻 | 高性能,簡(jiǎn)單高效 | 不可動(dòng)態(tài)更新,缺乏靈活性 | 服務(wù)器級(jí)別全局控制 |
三、完整實(shí)現(xiàn)方案
3.1 自定義 Filter 實(shí)現(xiàn) IP 白名單
1.1 添加依賴
Spring Boot 默認(rèn)支持 Servlet API,無(wú)需額外依賴。
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)訪問(wè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(); // 如果沒(méi)有代理,直接獲取遠(yuǎn)程地址 } return ip; } @Override public void destroy() { // 清理資源 } }
1.4 注冊(cè) Filter(可選)
如果未使用 @Component
注解,可通過(guò)配置類注冊(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)訪問(wè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ù)庫(kù) + 緩存)
4.1 添加依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
4.2 創(chuàng)建數(shù)據(jù)庫(kù)表
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ù)庫(kù) 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)訪問(wè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簡(jiǎn)化代碼 --> <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); // 如果都沒(méi)有注解,則跳過(guò)檢查 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 獲取問(wèn)題:
- 如果使用 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ù)庫(kù)查詢。
- 高并發(fā)場(chǎng)景下,F(xiàn)ilter 或 AOP 的性能開銷需評(píng)估。
安全性:
- 確保白名單邏輯不被繞過(guò)(如偽造
X-Forwarded-For
頭)。 - 結(jié)合其他安全措施(如 HTTPS、JWT)增強(qiáng)防護(hù)。
- 確保白名單邏輯不被繞過(guò)(如偽造
四、進(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ì)記錄所有被拒絕的訪問(wèn)嘗試
- 監(jiān)控告警:對(duì)異常訪問(wèn)模式設(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 故障排查指南
常見問(wèn)題及解決方案:
問(wèn)題現(xiàn)象 | 可能原因 | 解決方案 |
---|---|---|
合法IP被拒絕 | IP獲取不正確 | 檢查X-Forwarded-For等頭部配置 |
白名單修改后不生效 | 緩存未刷新 | 實(shí)現(xiàn)配置變更通知機(jī)制 |
性能下降 | IP檢查成為瓶頸 | 引入緩存、優(yōu)化算法或水平擴(kuò)展 |
分布式環(huán)境不一致 | 各節(jié)點(diǎn)白名單不同步 | 使用集中式存儲(chǔ)如Redis |
IPv6支持問(wèn)題 | 只配置了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)變化的問(wèn)題:
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 未來(lái)演進(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)境適配。通過(guò)本教程,您應(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ā)揮著不可替代的作用。未來(lái),隨著邊緣計(jì)算和5G技術(shù)的普及,IP白名單技術(shù)可能會(huì)與更多新興技術(shù)融合,形成更智能、更靈活的訪問(wèn)控制體系。
到此這篇關(guān)于SpringBoot中IP白名單控制實(shí)現(xiàn)限制接口訪問(wè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-07SpringBoot3整合Nacos?V2.3.2的詳細(xì)過(guò)程
本文介紹了如何在?Spring?Boot?3.2.x?項(xiàng)目中整合?Nacos?2.3.2,包括依賴配置、Nacos?服務(wù)發(fā)現(xiàn)與動(dòng)態(tài)配置的配置方法,通過(guò)整合?Nacos,Spring?Boot?應(yīng)用可以實(shí)現(xiàn)高效的服務(wù)發(fā)現(xiàn)、動(dòng)態(tài)配置管理以及分布式系統(tǒng)中的靈活擴(kuò)展,感興趣的朋友跟隨小編一起看看吧2024-11-11springboot中獲取配置文件中屬性值的幾種方式小結(jié)
本文主要介紹了springboot中獲取配置文件中屬性值的幾種方式小結(jié),主要介紹了六種方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05Maven配置項(xiàng)目依賴使用本地倉(cāng)庫(kù)的方法匯總(小結(jié))
這篇文章主要介紹了Maven配置項(xiàng)目依賴使用本地倉(cāng)庫(kù)的方法匯總(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07Java實(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屬性,用來(lái)防止 CSRF 攻擊和用戶追蹤。今天通過(guò)本文給大家介紹Springboot應(yīng)用中設(shè)置Cookie的SameSite屬性,感興趣的朋友一起看看吧2022-01-01