Spring Boot IP 封禁系统实现
这是一个基于 Spring Boot 和 Redisson 的 IP 封禁系统,支持三种封禁类型(全局 IP 封禁、接口封禁、IP+接口封禁),通过单一注解 @Ban
实现,结合白名单和黑名单功能。白名单和黑名单通过 application.yml
配置,不使用 Redis 存储,其他封禁信息存储在 Redis 中。
功能概述
- 封禁类型:
- 全局 IP 封禁:通过
@Ban(type = BanType.IP)
或全局开关(ip.ban.global-enabled=true
),限制特定 IP 访问所有接口。 - 接口封禁:通过
@Ban(type = BanType.INTERFACE)
,限制所有 IP 访问特定接口。 - IP+接口封禁:通过
@Ban(type = BanType.IP_INTERFACE)
,限制特定 IP 访问特定接口。
- 全局 IP 封禁:通过
- 自动封禁:通过注解配置时间窗口(
windowSeconds
)、最大请求次数(maxRequests
)、封禁时长(banDuration
)和时间单位(timeUnit
),超出频率触发自动封禁。 - 白名单:在
application.yml
配置,IP 免受所有封禁检查,直接放行。 - 黑名单:在
application.yml
配置,IP 直接全局封禁。 - 优先级:白名单 > 黑名单 > 全局 IP 封禁 > 注解封禁。
- Redis 键:
- IP 封禁:
banned:ips
- 接口封禁:
banned:interfaces
- IP+接口封禁:
banned:ip:interface:<ip>&<interface>
- 请求计数:
rate:limit:<ip/interface/ip&interface>
- IP 封禁:
- 执行顺序:全局检查(白名单、黑名单、全局 IP 封禁)先于
@Ban
注解检查。
代码实现
1. 封禁类型枚举 (BanType.java
)
定义封禁类型枚举。
package com.minimalism.redis.ban;
public enum BanType {
IP, // 全局 IP 封禁
INTERFACE, // 接口封禁
IP_INTERFACE // IP+接口封禁
}
2. 自定义注解 (Ban.java
)
定义 @Ban
注解,包含封禁类型和自动封禁参数。
package com.minimalism.redis.aop.ban;
import com.minimalism.redis.ban.BanType;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Ban {
BanType type() default BanType.IP; // 封禁类型
long windowSeconds() default 60; // 请求时间窗口(秒)
long maxRequests() default 10; // 最大请求次数
long banDuration() default 3600; // 自动封禁时长
TimeUnit timeUnit() default TimeUnit.SECONDS; // 时间单位
}
3. 配置文件 (application.yml
)
配置白名单、黑名单和全局 IP 封禁开关。
ip:
ban:
global-enabled: false # 全局 IP 封禁开关,默认禁用
whitelist: 192.168.1.100,192.168.1.101 # 白名单 IP,逗号分隔
blacklist: 192.168.1.200,192.168.1.201 # 黑名单 IP,逗号分隔
4. Redisson 配置 (RedissonConfig.java
)
配置 Redisson 客户端。
package com.minimalism.redis.config;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
// config.setPassword("your_password"); // 如果需要密码
return Redisson.create(config);
}
}
5. 抽象切面接口 (AbstractRedisAspect.java
)
定义抽象切面接口。
package com.minimalism.abstractinterface.aop;
import org.aspectj.lang.ProceedingJoinPoint;
public interface AbstractRedisAspect {
void pointcutAspect();
default Object around(ProceedingJoinPoint joinPoint) throws Throwable {
return joinPoint.proceed();
}
}
6. 封禁管理接口 (BanManager.java
)
定义封禁管理接口。
package com.minimalism.abstractinterface.ban;
import com.minimalism.redis.ban.BanType;
import java.util.concurrent.TimeUnit;
public interface BanManager {
void banIP(String ipAddress, long duration, TimeUnit timeUnit);
boolean isIPBanned(String ipAddress);
void unbanIP(String ipAddress);
void banInterface(String interfaceName, long duration, TimeUnit timeUnit);
boolean isInterfaceBanned(String interfaceName);
void unbanInterface(String interfaceName);
void banIPInterface(String ipAddress, String interfaceName, long duration, TimeUnit timeUnit);
boolean isIPInterfaceBanned(String ipAddress, String interfaceName);
void unbanIPInterface(String ipAddress, String interfaceName);
boolean checkAndBan(String key, long windowSeconds, long maxRequests, long banDuration, TimeUnit timeUnit, BanType banType, String ipAddress, String interfaceName);
}
7. 封禁管理实现 (SimpleBanManager.java
)
实现封禁和请求计数逻辑。
package com.minimalism.redis.ban;
import com.minimalism.abstractinterface.ban.BanManager;
import org.redisson.api.RAtomicLong;
import org.redisson.api.RSet;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class SimpleBanManager implements BanManager {
private final RedissonClient redissonClient;
private static final String BANNED_IPS_KEY = "banned:ips"; // 全局 IP 封禁
private static final String BANNED_INTERFACES_KEY = "banned:interfaces"; // 接口封禁
private static final String BANNED_IP_INTERFACE_PREFIX = "banned:ip:interface:"; // IP+接口封禁前缀
private static final String RATE_LIMIT_PREFIX = "rate:limit:"; // 请求计数前缀
public SimpleBanManager(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
@Override
public void banIP(String ipAddress, long duration, TimeUnit timeUnit) {
RSet<String> bannedIps = redissonClient.getSet(BANNED_IPS_KEY);
bannedIps.add(ipAddress);
if (duration > 0) {
bannedIps.expire(duration, timeUnit);
}
}
@Override
public boolean isIPBanned(String ipAddress) {
RSet<String> bannedIps = redissonClient.getSet(BANNED_IPS_KEY);
return bannedIps.contains(ipAddress);
}
@Override
public void unbanIP(String ipAddress) {
RSet<String> bannedIps = redissonClient.getSet(BANNED_IPS_KEY);
bannedIps.remove(ipAddress);
}
@Override
public void banInterface(String interfaceName, long duration, TimeUnit timeUnit) {
RSet<String> bannedInterfaces = redissonClient.getSet(BANNED_INTERFACES_KEY);
bannedInterfaces.add(interfaceName);
if (duration > 0) {
bannedInterfaces.expire(duration, timeUnit);
}
}
@Override
public boolean isInterfaceBanned(String interfaceName) {
RSet<String> bannedInterfaces = redissonClient.getSet(BANNED_INTERFACES_KEY);
return bannedInterfaces.contains(interfaceName);
}
@Override
public void unbanInterface(String interfaceName) {
RSet<String> bannedInterfaces = redissonClient.getSet(BANNED_INTERFACES_KEY);
bannedInterfaces.remove(interfaceName);
}
@Override
public void banIPInterface(String ipAddress, String interfaceName, long duration, TimeUnit timeUnit) {
String key = BANNED_IP_INTERFACE_PREFIX + ipAddress + "&" + interfaceName;
redissonClient.getBucket(key).set(true);
if (duration > 0) {
redissonClient.getBucket(key).expire(duration, timeUnit);
}
}
@Override
public boolean isIPInterfaceBanned(String ipAddress, String interfaceName) {
String key = BANNED_IP_INTERFACE_PREFIX + ipAddress + "&" + interfaceName;
return redissonClient.getBucket(key).isExists();
}
@Override
public void unbanIPInterface(String ipAddress, String interfaceName) {
String key = BANNED_IP_INTERFACE_PREFIX + ipAddress + "&" + interfaceName;
redissonClient.getBucket(key).delete();
}
@Override
public boolean checkAndBan(String key, long windowSeconds, long maxRequests, long banDuration, TimeUnit timeUnit, BanType banType, String ipAddress, String interfaceName) {
String rateKey = RATE_LIMIT_PREFIX + key;
RAtomicLong counter = redissonClient.getAtomicLong(rateKey);
long count = counter.incrementAndGet();
if (count == 1) {
counter.expire(windowSeconds, TimeUnit.SECONDS);
}
if (count > maxRequests) {
switch (banType) {
case IP:
banIP(ipAddress, banDuration, timeUnit);
break;
case INTERFACE:
banInterface(interfaceName, banDuration, timeUnit);
break;
case IP_INTERFACE:
banIPInterface(ipAddress, interfaceName, banDuration, timeUnit);
break;
}
return true; // 已封禁
}
return false; // 未封禁
}
}
8. 封禁配置类 (BanConfiguration.java
)
管理白名单、黑名单和全局封禁开关。
package com.minimalism.redis.ban;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
@Component
public class BanConfiguration {
@Value("${ip.ban.global-enabled:false}")
private boolean globalBanEnabled;
@Value("${ip.whitelist:}")
private String whitelistIps;
@Value("${ip.blacklist:}")
private String blacklistIps;
private final Set<String> whitelist;
private final Set<String> blacklist;
public BanConfiguration() {
this.whitelist = new HashSet<>(Arrays.asList(whitelistIps.split(",")));
this.blacklist = new HashSet<>(Arrays.asList(blacklistIps.split(",")));
this.whitelist.remove("");
this.blacklist.remove("");
}
public boolean isGlobalBanEnabled() {
return globalBanEnabled;
}
public Set<String> getWhitelist() {
return whitelist;
}
public Set<String> getBlacklist() {
return blacklist;
}
}
9. 自定义异常 (BanException.java
)
定义封禁异常。
package com.minimalism.redis.exception;
public class BanException extends RuntimeException {
public BanException(String message) {
super(message);
}
}
10. AOP 切面 (RedisBanAspect.java
)
实现白名单、黑名单、全局封禁和注解检查逻辑,确保全局检查先执行。
package com.minimalism.redis.aop.aspect;
import cn.hutool.extra.spring.SpringUtil;
import com.minimalism.abstractinterface.aop.AbstractRedisAspect;
import com.minimalism.abstractinterface.ban.BanManager;
import com.minimalism.redis.aop.ban.Ban;
import com.minimalism.redis.ban.BanConfiguration;
import com.minimalism.redis.ban.SimpleBanManager;
import com.minimalism.redis.ban.BanType;
import com.minimalism.redis.exception.BanException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
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.util.Set;
import static cn.hutool.extra.servlet.ServletUtil.getClientIP;
@Aspect
@Slf4j
@Component
public class RedisBanAspect implements AbstractRedisAspect {
static SimpleBanManager DEFAULT_BAN_MANAGER;
static BanConfiguration DEFAULT_BAN_CONFIGURATION;
static {
try {
DEFAULT_BAN_MANAGER = SpringUtil.getBean(SimpleBanManager.class);
} catch (NoSuchBeanDefinitionException e) {
log.warn("SimpleBanManager not found, creating default instance: {}", e.getMessage());
DEFAULT_BAN_MANAGER = new SimpleBanManager(SpringUtil.getBean(RedissonClient.class));
}
try {
DEFAULT_BAN_CONFIGURATION = SpringUtil.getBean(BanConfiguration.class);
} catch (NoSuchBeanDefinitionException e) {
log.warn("BanConfiguration not found, creating default instance: {}", e.getMessage());
DEFAULT_BAN_CONFIGURATION = new BanConfiguration();
}
}
@Override
@Pointcut("@annotation(com.minimalism.redis.aop.ban.Ban)")
public void pointcutAspect() {
}
@Override
@Around("execution(* com.minimalism..*Controller.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("[{}] 执行全局检查", System.currentTimeMillis());
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String ipAddress = getClientIP(request);
BanConfiguration banConfiguration = DEFAULT_BAN_CONFIGURATION;
boolean globalBanEnabled = banConfiguration.isGlobalBanEnabled();
Set<String> whitelist = banConfiguration.getWhitelist();
Set<String> blacklist = banConfiguration.getBlacklist();
// 检查白名单
if (whitelist.contains(ipAddress)) {
log.info("IP {} 在白名单中,直接放行", ipAddress);
return joinPoint.proceed();
}
// 检查黑名单
if (blacklist.contains(ipAddress)) {
log.warn("IP {} 在黑名单中,拒绝访问", ipAddress);
throw new BanException("IP 地址 " + ipAddress + " 在黑名单中");
}
BanManager banManager = DEFAULT_BAN_MANAGER;
// 检查全局 IP 封禁
if (globalBanEnabled && banManager.isIPBanned(ipAddress)) {
log.warn("IP {} 已被全局封禁", ipAddress);
throw new BanException("IP 地址 " + ipAddress + " 已被全局封禁");
}
// 检查 @Ban 注解(仅对带有 @Ban 注解的方法执行)
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Ban ban = signature.getMethod().getAnnotation(Ban.class);
if (ban != null) {
log.info("[{}] 执行 @Ban 注解检查", System.currentTimeMillis());
String interfaceName = joinPoint.getSignature().toString();
BanType banType = ban.type();
long windowSeconds = ban.windowSeconds();
long maxRequests = ban.maxRequests();
long banDuration = ban.banDuration();
java.util.concurrent.TimeUnit timeUnit = ban.timeUnit();
String rateKey;
switch (banType) {
case IP:
rateKey = ipAddress;
if (banManager.checkAndBan(rateKey, windowSeconds, maxRequests, banDuration, timeUnit, banType, ipAddress, interfaceName)
|| banManager.isIPBanned(ipAddress)) {
log.warn("IP {} 已被封禁", ipAddress);
throw new BanException("IP 地址 " + ipAddress + " 已被封禁");
}
break;
case INTERFACE:
rateKey = interfaceName;
if (banManager.checkAndBan(rateKey, windowSeconds, maxRequests, banDuration, timeUnit, banType, ipAddress, interfaceName)
|| banManager.isInterfaceBanned(interfaceName)) {
log.warn("接口 {} 已被封禁", interfaceName);
throw new BanException("接口 " + interfaceName + " 已被封禁");
}
break;
case IP_INTERFACE:
rateKey = ipAddress + "&" + interfaceName;
if (banManager.checkAndBan(rateKey, windowSeconds, maxRequests, banDuration, timeUnit, banType, ipAddress, interfaceName)
|| banManager.isIPInterfaceBanned(ipAddress, interfaceName)) {
log.warn("IP {} 在接口 {} 上被封禁", ipAddress, interfaceName);
throw new BanException("IP 地址 " + ipAddress + " 在接口 " + interfaceName + " 上被封禁");
}
break;
}
}
return joinPoint.proceed();
}
}
11. 测试控制器 (TestController.java
)
展示白名单、黑名单和三种封禁的使用。
package com.minimalism.redis.controller;
import com.minimalism.redis.aop.ban.Ban;
import com.minimalism.redis.ban.BanType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/api")
public class TestController {
@Ban(type = BanType.IP, windowSeconds = 60, maxRequests = 5, banDuration = 3600, timeUnit = TimeUnit.SECONDS)
@GetMapping("/ip-ban")
public String ipBanEndpoint() {
return "访问成功(IP 封禁检查,60秒内超5次请求封禁1小时)!";
}
@Ban(type = BanType.INTERFACE, windowSeconds = 60, maxRequests = 10, banDuration = 1800, timeUnit = TimeUnit.SECONDS)
@GetMapping("/interface-ban")
public String interfaceBanEndpoint() {
return "访问成功(接口封禁检查,60秒内超10次请求封禁30分钟)!";
}
@Ban(type = BanType.IP_INTERFACE, windowSeconds = 30, maxRequests = 3, banDuration = 600, timeUnit = TimeUnit.SECONDS)
@GetMapping("/ip-interface-ban")
public String ipInterfaceBanEndpoint() {
return "访问成功(IP+接口封禁检查,30秒内超3次请求封禁10分钟)!";
}
@GetMapping("/no-ban")
public String noBanEndpoint() {
return "此接口仅受全局 IP 封禁、黑名单或白名单影响";
}
}
12. 管理员控制器 (AdminController.java
)
管理封禁(白名单和黑名单通过配置文件管理)。
package com.minimalism.redis.controller;
import com.minimalism.abstractinterface.ban.BanManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/admin")
public class AdminController {
@Autowired
private BanManager banManager;
@GetMapping("/ban-ip")
public String banIP(@RequestParam String ip, @RequestParam(defaultValue = "0") long duration, @RequestParam(defaultValue = "SECONDS") String timeUnit) {
banManager.banIP(ip, duration, TimeUnit.valueOf(timeUnit));
return "IP " + ip + " 已全局封禁";
}
@GetMapping("/unban-ip")
public String unbanIP(@RequestParam String ip) {
banManager.unbanIP(ip);
return "IP " + ip + " 已解除全局封禁";
}
@GetMapping("/ban-interface")
public String banInterface(@RequestParam String interfaceName, @RequestParam(defaultValue = "0") long duration, @RequestParam(defaultValue = "SECONDS") String timeUnit) {
banManager.banInterface(interfaceName, duration, TimeUnit.valueOf(timeUnit));
return "接口 " + interfaceName + " 已封禁";
}
@GetMapping("/unban-interface")
public String unbanInterface(@RequestParam String interfaceName) {
banManager.unbanInterface(interfaceName);
return "接口 " + interfaceName + " 已解除封禁";
}
@GetMapping("/ban-ip-interface")
public String banIPInterface(@RequestParam String ip, @RequestParam String interfaceName, @RequestParam(defaultValue = "0") long duration, @RequestParam(defaultValue = "SECONDS") String timeUnit) {
banManager.banIPInterface(ip, interfaceName, duration, TimeUnit.valueOf(timeUnit));
return "IP " + ip + " 在接口 " + interfaceName + " 上已封禁";
}
@GetMapping("/unban-ip-interface")
public String unbanIPInterface(@RequestParam String ip, @RequestParam String interfaceName) {
banManager.unbanIPInterface(ip, interfaceName);
return "IP " + ip + " 在接口 " + interfaceName + " 上已解除封禁";
}
}
13. 全局异常处理 (GlobalExceptionHandler.java
)
处理封禁异常。
package com.minimalism.redis.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BanException.class)
public ResponseEntity<String> handleBanException(BanException ex) {
return new ResponseEntity<>(ex.getMessage(), HttpStatus.FORBIDDEN);
}
}
14. Maven 依赖 (pom.xml
片段)
确保添加必要的依赖。
<dependencies>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.36.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.28</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.34</version>
<scope>provided</scope>
</dependency>
</dependencies>
使用说明
1. 环境准备
- Redis:确保 Redis 运行在
localhost:6379
,必要时配置密码。 - Spring Boot:确保项目包含上述 Maven 依赖。
- 配置文件:修改
application.yml
,设置白名单、黑名单和全局封禁开关。
2. 白名单和黑名单
- 白名单:在
application.yml
的ip.whitelist
配置 IP(逗号分隔,如192.168.1.100,192.168.1.101
),免受所有封禁检查。 - 黑名单:在
application.yml
的ip.blacklist
配置 IP(逗号分隔,如192.168.1.200,192.168.1.201
),直接全局封禁。 - 管理:修改
application.yml
后需重启应用。 - 优先级:白名单 > 黑名单 > 全局 IP 封禁 > 注解封禁。
3. 封禁类型
- 全局 IP 封禁:
- 注解:
@Ban(type = BanType.IP, windowSeconds = 60, maxRequests = 5, banDuration = 3600, timeUnit = TimeUnit.SECONDS)
- 触发:60秒内超过5次请求,封禁1小时。
- 全局开关:
ip.ban.global-enabled=true
- 注解:
- 接口封禁:
- 注解:
@Ban(type = BanType.INTERFACE, windowSeconds = 60, maxRequests = 10, banDuration = 1800, timeUnit = TimeUnit.SECONDS)
- 触发:60秒内超过10次请求,封禁30分钟。
- 注解:
- IP+接口封禁:
- 注解:
@Ban(type = BanType.IP_INTERFACE, windowSeconds = 30, maxRequests = 3, banDuration = 600, timeUnit = TimeUnit.SECONDS)
- 触发:30秒内超过3次请求,封禁10分钟。
- 注解:
4. 测试流程
- 启动 Redis:确保 Redis 运行(
localhost:6379
)。 - 白名单:
- 配置
ip.whitelist=192.168.1.100
,IP192.168.1.100
访问/api/ip-ban
等接口均放行。
- 配置
- 黑名单:
- 配置
ip.blacklist=192.168.1.200
,IP192.168.1.200
访问任何接口返回 403。
- 配置
- 全局 IP 封禁:
- 访问
/api/ip-ban
超过5次(60秒内),触发1小时封禁。 - 或调用
/admin/ban-ip?ip=192.168.1.100&duration=3600&timeUnit=SECONDS
。 - 启用
ip.ban.global-enabled=true
,访问任何接口返回 403。
- 访问
- 接口封禁:
- 访问
/api/interface-ban
超过10次(60秒内),触发30分钟封禁。 - 或调用
/admin/ban-interface?interfaceName=public java.lang.String com.minimalism.redis.controller.TestController.interfaceBanEndpoint()&duration=1800&timeUnit=SECONDS
。
- 访问
- IP+接口封禁:
- 访问
/api/ip-interface-ban
超过3次(30秒内),触发10分钟封禁。 - 或调用
/admin/ban-ip-interface?ip=192.168.1.100&interfaceName=public java.lang.String com.minimalism.redis.controller.TestController.ipInterfaceBanEndpoint()&duration=600&timeUnit=SECONDS
。
- 访问
- 解除封禁:
- 使用
/admin/unban-ip
、/admin/unban-interface
或/admin/unban-ip-interface
。
- 使用
- 验证执行顺序:
- 检查日志,确认全局检查日志(
执行全局检查
)先于注解检查日志(执行 @Ban 注解检查
)。
- 检查日志,确认全局检查日志(
5. 接口名称
- 默认使用方法签名(如
public java.lang.String com.minimalism.redis.controller.TestController.interfaceBanEndpoint()
)。 - 建议简化为请求路径(如
/api/test
),修改interfaceName
获取逻辑(如在RedisBanAspect
中使用request.getRequestURI()
)。
注意事项
- 白名单/黑名单:静态配置,修改
application.yml
需重启。如需动态管理,可扩展AdminController
使用 Redis。 - IP 获取:使用
Hutool
的ServletUtil.getClientIP
,支持代理情况。 - Redis 配置:确保 Redis 可访问,必要时配置密码或防火墙。
- 性能:Redisson 的
RSet
,RBucket
, 和RAtomicLong
适合高并发,连接池可优化。 - 安全性:避免 Redis 公网暴露,使用私有端点或认证。
- 日志:使用
lombok
的@Slf4j
记录封禁检查日志。 - 时间单位:支持
TimeUnit
(如SECONDS
,MINUTES
),灵活配置。 - 执行顺序:全局检查(白名单、黑名单、全局 IP 封禁)先于
@Ban
注解检查,确保优先级正确。
扩展建议
- 动态名单管理:添加
/admin/add-whitelist
等端点,使用 Redis 存储动态白名单/黑名单。 - 日志记录:记录封禁事件到日志或数据库。
- Redis Cluster:支持高可用 Redis 集群配置。
- 复杂策略:支持基于用户角色或请求参数的封禁规则。
- 接口名称优化:使用
request.getRequestURI()
获取路径(如/api/test
)替代方法签名,提升可读性。
参考
- xAI API:如需 API 集成,参考 https://x.ai/api.
- Redisson 文档:查看 Redisson 配置和性能优化。
- Hutool 文档:查看
ServletUtil
和SpringUtil
使用方法。