优化黑名单验证
This commit is contained in:
@@ -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:";
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
if (duration.getSeconds() > intervalTime) {
|
||||
replayInfo = new RedisPreventReplayInfo();
|
||||
}
|
||||
|
||||
// 增加次数 写回redis
|
||||
// 更新最近一次请求时间并计数
|
||||
replayInfo.setLastTiming(Instant.now());
|
||||
replayInfo.setFrequency(replayInfo.getFrequency() + 1);
|
||||
redisServerTool.set(redisKeyName, replayInfo);
|
||||
|
||||
boolean type = replayInfo.getFrequency() <= limitNumber;
|
||||
// 写回 Redis,设置 TTL 与窗口保持一致,防止孤儿键占用空间
|
||||
redisServerTool.set(redisKeyName, replayInfo, intervalTime, TimeUnit.SECONDS);
|
||||
|
||||
// 加黑
|
||||
if (!type) {
|
||||
redisServerTool.set(PreRedisKeyName.BLACK_LIST + request.getRemoteAddr(), "高频请求", 1, TimeUnit.DAYS);
|
||||
boolean allowed = replayInfo.getFrequency() <= limitNumber;
|
||||
if (!allowed) {
|
||||
// 加入黑名单,使用相同指纹键避免误拦
|
||||
redisServerTool.set(PreRedisKeyName.BLACK_LIST + generateFingerprint(request), "高频请求", 1, TimeUnit.DAYS);
|
||||
response.setStatus(HttpServletResponse.SC_CONFLICT);
|
||||
}
|
||||
|
||||
return type;
|
||||
return allowed;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user