修复防重放拦截器时间戳兼容与日志

This commit is contained in:
root
2026-03-09 20:24:39 +08:00
parent 46ac106f9b
commit 5caf9e219a

View File

@@ -23,10 +23,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
@Slf4j
public class ReplayAttackInterceptor implements HandlerInterceptor {
// 请求时间戳有效期(毫秒):5分钟
private static final long TIMESTAMP_VALIDITY = 5 * 60 * 1000;
// nonce 有效期毫秒10分钟
private static final long NONCE_EXPIRE_TIME = 10 * 60 * 1000;
// 请求时间戳有效期(毫秒):10分钟兼容移动端网络抖动与时钟偏差
private static final long TIMESTAMP_VALIDITY = 10 * 60 * 1000;
// 允许少量未来时间偏移毫秒2分钟兼容秒级时间戳和终端时钟轻微超前
private static final long ALLOWED_FUTURE_DRIFT = 2 * 60 * 1000;
// nonce 有效期毫秒15分钟
private static final long NONCE_EXPIRE_TIME = 15 * 60 * 1000;
// 最大存储 nonce 数(防止内存溢出)
private static final int MAX_NONCES = 100000;
@@ -91,24 +93,35 @@ public class ReplayAttackInterceptor implements HandlerInterceptor {
String timestampStr = request.getHeader("X-Timestamp");
String nonce = request.getHeader("X-Nonce");
String userAgent = request.getHeader("User-Agent");
// 检查参数是否存在
if (timestampStr == null || timestampStr.isEmpty() || nonce == null || nonce.isEmpty()) {
String missingHeader = (timestampStr == null || timestampStr.isEmpty()) && (nonce == null || nonce.isEmpty())
? "X-Timestamp,X-Nonce"
: ((timestampStr == null || timestampStr.isEmpty()) ? "X-Timestamp" : "X-Nonce");
log.warn("防重放校验失败: uri={}, method={}, ua={}, missingHeaders={}",
requestUri, request.getMethod(), userAgent, missingHeader);
writeErrorResponse(response, "缺少安全验证参数");
return false;
}
// 验证时间戳
// 验证时间戳(兼容 10 位秒级与 13 位毫秒级)
long timestamp;
try {
timestamp = Long.parseLong(timestampStr);
timestamp = normalizeTimestamp(timestampStr);
} catch (NumberFormatException e) {
log.warn("防重放校验失败: uri={}, method={}, ua={}, invalidTimestamp={}, error={}",
requestUri, request.getMethod(), userAgent, timestampStr, e.getMessage());
writeErrorResponse(response, "无效的时间戳参数");
return false;
}
long now = System.currentTimeMillis();
// 修复:不允许未来时间戳,防止预发送攻击
if (timestamp > now || now - timestamp > TIMESTAMP_VALIDITY) {
long timeDiff = now - timestamp;
if (timeDiff < -ALLOWED_FUTURE_DRIFT || timeDiff > TIMESTAMP_VALIDITY) {
log.warn("防重放校验失败: uri={}, method={}, ua={}, timestamp={}, normalizedTimestamp={}, now={}, diffMs={}, validityMs={}",
requestUri, request.getMethod(), userAgent, timestampStr, timestamp, now, timeDiff, TIMESTAMP_VALIDITY);
writeErrorResponse(response, "请求时间戳无效或已过期");
return false;
}
@@ -117,6 +130,8 @@ public class ReplayAttackInterceptor implements HandlerInterceptor {
lock.readLock().lock();
try {
if (usedNonces.containsKey(nonce)) {
log.warn("防重放校验失败: uri={}, method={}, ua={}, nonce={}, reason=reused_nonce",
requestUri, request.getMethod(), userAgent, nonce);
writeErrorResponse(response, "检测到重放攻击,请求已被拒绝");
return false;
}
@@ -135,6 +150,15 @@ public class ReplayAttackInterceptor implements HandlerInterceptor {
return true;
}
private long normalizeTimestamp(String timestampStr) {
long parsedTimestamp = Long.parseLong(timestampStr);
if (timestampStr.length() == 10) {
return parsedTimestamp * 1000;
}
return parsedTimestamp;
}
/**
* 清理过期的 nonce
*/