Springboot中基于 IP 地址的请求速率限制拦截器
基于 IP 地址的请求速率限制拦截器,使用了 Bucket4j 库来管理请求的令牌桶。下面是对代码的详细解释,以及如何在触发请求拒绝时将 IP 地址加入黑名单的实现。
导入依赖
<dependency>
<groupId>com.github.vladimir-bukhtoyarov</groupId>
<artifactId>bucket4j-core</artifactId>
<version>6.0.2</version>
</dependency>
RateLimitInterceptor拦截器类
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Bucket;
import io.github.bucket4j.Bucket4j;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
public class RateLimitInterceptor implements HandlerInterceptor {
private final Bandwidth limit = Bandwidth.simple(100, Duration.ofMinutes(1));
private final ConcurrentHashMap<String, Bucket> buckets = new ConcurrentHashMap<>();
@Autowired
private RedisTemplate<String, String> redisTemplate; // Redis 模板
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String ip = request.getRemoteAddr();
log.info("当前访问ip: {}", ip);
// 检查 IP 是否在黑名单中
if (isBlacklisted(ip)) {
response.setStatus(403); // 返回 403 状态码,表示禁止访问
return false; // 拒绝请求
}
Bucket bucket = buckets.computeIfAbsent(ip, key -> Bucket4j.builder().addLimit(limit).build());
if (bucket.tryConsume(1)) {
return true; // 允许请求
} else {
// 将 IP 加入黑名单
blacklistIp(ip);
response.setStatus(429); // 返回 429 状态码
return false; // 拒绝请求
}
}
private boolean isBlacklisted(String ip) {
// 检查 Redis 中是否存在该 IP
return redisTemplate.hasKey("blacklist:" + ip);
}
private void blacklistIp(String ip) {
// 将 IP 加入黑名单,设置过期时间为 1 小时
redisTemplate.opsForValue().set("blacklist:" + ip, "true", Duration.ofHours(1));
}
}
导入依赖:
Bucket4j:用于实现令牌桶算法的库。
HandlerInterceptor:Spring MVC 的拦截器接口,用于在请求处理之前和之后执行一些操作。
ConcurrentHashMap:用于存储每个 IP 地址对应的令牌桶,支持并发访问。
类定义:
RateLimitInterceptor 类实现了 HandlerInterceptor 接口。
带宽定义:
limit:定义了一个带宽限制,设置为每分钟允许 100 次请求。
桶存储:
buckets:使用 ConcurrentHashMap 存储每个 IP 地址对应的令牌桶。
请求处理:
preHandle 方法在请求处理之前被调用。
获取请求的 IP 地址。
使用 computeIfAbsent 方法获取或创建对应 IP 的桶。
尝试消耗一个令牌,如果成功,则允许请求继续;如果失败,则返回 429 状态码,表示请求过多。
RedisTemplate:
使用 RedisTemplate 来与 Redis 进行交互,检查和存储黑名单。
黑名单检查:
在 preHandle 方法中,首先检查请求的 IP 是否在黑名单中。如果在黑名单中,返回 403 状态码,拒绝请求。
黑名单添加:
如果请求被拒绝(即超过速率限制),调用 blacklistIp 方法将该 IP 加入黑名单,并设置过期时间为 1 小时。
黑名单检查方法:
isBlacklisted 方法用于检查 IP 是否在黑名单中。
通过这些修改,您可以有效地管理请求速率,并在触发拒绝请求时将 IP 地址加入黑名单,从而防止恶意请求。
web配置文件中注册拦截器
/**
* 注册拦截器
* @Param [registry]
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册jwt拦截器
registry.addInterceptor(new JWTInterceptor()).
excludePathPatterns("/user/register", "/user/login", "/user/info", "/user/logout", ).//请求放行
addPathPatterns("/**");
// 注册速率限制拦截器
registry.addInterceptor(new RateLimitInterceptor());
}