题目
高并发场景下如何设计支持幂等性和防重放攻击的Spring MVC接口
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
Spring MVC请求处理流程, 分布式锁实现, 幂等性设计, 防重放攻击, 高并发优化
快速回答
实现要点:
- 使用唯一请求ID配合Redis分布式锁保证并发幂等性
- 通过时间戳+签名机制防御重放攻击
- 采用二级缓存策略(内存缓存+Redis)优化性能
- 使用异步处理分离核心业务逻辑
- 实现自动清理机制防止存储膨胀
1. 核心问题分析
在高并发场景下需同时解决:
幂等性 - 相同请求多次执行结果一致
防重放 - 阻止恶意重复提交历史请求
性能 - 高并发下保持系统稳定
2. 架构设计
@RestController
public class PaymentController {
@PostMapping("/pay")
public ResponseEntity<?> processPayment(
@RequestHeader("X-Request-ID") String requestId,
@RequestHeader("X-Timestamp") long timestamp,
@RequestHeader("X-Signature") String signature,
@RequestBody PaymentRequest request) {
// 1. 验证时间戳和签名
// 2. 检查请求ID是否已存在
// 3. 获取分布式锁
// 4. 执行业务逻辑
}
}3. 关键技术实现
3.1 防重放攻击
// 时间窗口验证(5分钟)
if (System.currentTimeMillis() - timestamp > 300_000) {
throw new ReplayAttackException("Expired request");
}
// 签名验证(HMAC-SHA256)
String computedSig = HmacUtils.hmacSha256Hex(API_SECRET, requestId + timestamp);
if (!computedSig.equals(signature)) {
throw new SecurityException("Invalid signature");
}3.2 幂等性保障
// 使用Redisson实现分布式锁
RLock lock = redissonClient.getLock("IDEMPOTENT_LOCK:" + requestId);
try {
if (!lock.tryLock(0, 5, TimeUnit.SECONDS)) {
throw new ConcurrentAccessException("Request processing");
}
// 检查请求ID是否存在(二级缓存优化)
if (idempotencyCache.contains(requestId)) {
return previousResponse; // 返回缓存结果
}
// 执行业务逻辑
PaymentResponse response = paymentService.process(request);
// 缓存结果(设置TTL)
idempotencyCache.put(requestId, response, 24, TimeUnit.HOURS);
} finally {
lock.unlock();
}3.3 高并发优化
- 二级缓存结构:Caffeine本地缓存(1秒)+ Redis分布式缓存
- 异步处理:
@Async注解分离非核心逻辑(如日志记录) - 限流机制:
在Controller层添加@RateLimiter注解
4. 最佳实践
- 请求ID生成:客户端使用UUIDv4或雪花算法
- 时钟同步:部署NTP服务保证服务器时间一致
- 密钥管理:使用KMS轮转API密钥
- 缓存清理:定时任务清理过期请求记录
5. 常见错误
- 锁超时问题:业务执行时间 > 锁超时时间导致并发穿透
- 缓存穿透:恶意伪造requestId耗尽存储
- 时间窗口漏洞:未校验时间戳顺序导致重放
- 签名缺陷:未包含关键参数导致篡改攻击
6. 扩展知识
- 分布式锁进阶:RedLock算法 vs Zookeeper顺序节点
- 性能压测:使用JMeter模拟万级并发验证方案
- 替代方案:基于数据库唯一索引实现幂等(需考虑分库分表)
- Spring集成:自定义
@Idempotent注解+AOP统一处理