优化黑名单验证

This commit is contained in:
zk
2026-03-10 20:17:40 +08:00
parent c4efa7e917
commit 49ba3929b2
2 changed files with 44 additions and 36 deletions
@@ -10,26 +10,16 @@ public interface PreRedisKeyName {
/**
* 防刷key名
*/
String PREVENT_REPLAY = "preventReplay:ip:";
/**
* 黑名单ip
*/
String BLACK_LIST = "blackList:ip:";
/**
* 单次执行任务Id
*/
String EXECUTE_SINGLE_TASK = "execute:single-task-id:";
String PREVENT_REPLAY = "preventReplay:hash:";
/**
* 黑名单指纹哈希值
*/
String BLACK_LIST = "blackList:hash:";
/**
* token Key名 前缀
*/
String LOGIN_TOKEN = "login:token:";
/**
* 图片验证码Key名 前缀
*/
String LOGIN_IMAGES_CODE_UUID = "login:images:uuid:";
}
@@ -19,7 +19,7 @@ import org.jiayunet.tool.server.RedisServerTool;
/**
* 防止高频重复请求
*
*
* @author zk
*/
@Component
@@ -43,36 +43,54 @@ public class PreventReplayInterceptor implements HandlerInterceptor {
@Autowired
private RedisServerTool redisServerTool;
/**
* 生成指纹:IP + User-Agent + Accept-Language
*/
private String generateFingerprint(HttpServletRequest request) {
String ip = HttpIpTool.gteRealIP(request);
String ua = request.getHeader("User-Agent");
String lang = request.getHeader("Accept-Language");
if (ua == null) ua = "";
if (lang == null) lang = "";
String raw = ip + "|" + ua + "|" + lang;
return Integer.toHexString(raw.hashCode());
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 关闭时 不执
if (!ifOpen) {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 关闭直接放
if (!Boolean.TRUE.equals(ifOpen)) {
return true;
}
String redisKeyName = PreRedisKeyName.PREVENT_REPLAY + HttpIpTool.gteRealIP(request);
// 使用指纹作为 Redis 键
String redisKeyName = PreRedisKeyName.PREVENT_REPLAY + generateFingerprint(request);
// 获取redis中的数据
// 读取计数信息
RedisPreventReplayInfo replayInfo = redisServerTool.get(redisKeyName, RedisPreventReplayInfo.class);
replayInfo = Objects.nonNull(replayInfo) ? replayInfo : new RedisPreventReplayInfo();
// 计算时间差 并更新数据
// 检查时间窗口是否已过
Duration duration = Duration.between(replayInfo.getLastTiming(), Instant.now());
long seconds = duration.getSeconds();
replayInfo = seconds <= intervalTime ? replayInfo : new RedisPreventReplayInfo();
// 增加次数 写回redis
replayInfo.setFrequency(replayInfo.getFrequency() + 1);
redisServerTool.set(redisKeyName, replayInfo);
boolean type = replayInfo.getFrequency() <= limitNumber;
// 加黑
if (!type) {
redisServerTool.set(PreRedisKeyName.BLACK_LIST + request.getRemoteAddr(), "高频请求", 1, TimeUnit.DAYS);
if (duration.getSeconds() > intervalTime) {
replayInfo = new RedisPreventReplayInfo();
}
return type;
// 更新最近一次请求时间并计数
replayInfo.setLastTiming(Instant.now());
replayInfo.setFrequency(replayInfo.getFrequency() + 1);
// 写回 Redis,设置 TTL 与窗口保持一致,防止孤儿键占用空间
redisServerTool.set(redisKeyName, replayInfo, intervalTime, TimeUnit.SECONDS);
boolean allowed = replayInfo.getFrequency() <= limitNumber;
if (!allowed) {
// 加入黑名单,使用相同指纹键避免误拦
redisServerTool.set(PreRedisKeyName.BLACK_LIST + generateFingerprint(request), "高频请求", 1, TimeUnit.DAYS);
response.setStatus(HttpServletResponse.SC_CONFLICT);
}
return allowed;
}
}