优化黑名单验证

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名 * 防刷key名
*/ */
String PREVENT_REPLAY = "preventReplay:ip:"; String PREVENT_REPLAY = "preventReplay:hash:";
/**
* 黑名单ip
*/
String BLACK_LIST = "blackList:ip:";
/**
* 单次执行任务Id
*/
String EXECUTE_SINGLE_TASK = "execute:single-task-id:";
/**
* 黑名单指纹哈希值
*/
String BLACK_LIST = "blackList:hash:";
/** /**
* token Key名 前缀 * token Key名 前缀
*/ */
String LOGIN_TOKEN = "login:token:"; 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 * @author zk
*/ */
@Component @Component
@@ -43,36 +43,54 @@ public class PreventReplayInterceptor implements HandlerInterceptor {
@Autowired @Autowired
private RedisServerTool redisServerTool; 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 @Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 关闭时 不执 // 关闭直接放
if (!ifOpen) { if (!Boolean.TRUE.equals(ifOpen)) {
return true; 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); RedisPreventReplayInfo replayInfo = redisServerTool.get(redisKeyName, RedisPreventReplayInfo.class);
replayInfo = Objects.nonNull(replayInfo) ? replayInfo : new RedisPreventReplayInfo(); replayInfo = Objects.nonNull(replayInfo) ? replayInfo : new RedisPreventReplayInfo();
// 计算时间差 并更新数据 // 检查时间窗口是否已过
Duration duration = Duration.between(replayInfo.getLastTiming(), Instant.now()); Duration duration = Duration.between(replayInfo.getLastTiming(), Instant.now());
long seconds = duration.getSeconds(); if (duration.getSeconds() > intervalTime) {
replayInfo = seconds <= intervalTime ? replayInfo : new RedisPreventReplayInfo(); 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);
} }
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;
} }
} }