接口防刷是保障系统安全与稳定性的重要措施,恶意高频请求可能导致服务器资源耗尽或数据异常。本文介绍SpringBoot中5种接口防刷方案,涵盖简单场景到复杂分布式系统的防护需求。
1. 基于注解的访问频率限制
核心思路:通过自定义注解和AOP切面,结合Redis实现请求频率控制。
实现步骤
1.1 创建限流注解@RateLimit
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimit { int time() default 60; // 限制时间段(秒) int count() default 10; // 时间段内最大请求次数 String key() default ""; // 限流Key(支持SpEL表达式) String message() default 操作太频繁,请稍后再试; }
1.2 实现限流切面RateLimitAspect
@Aspect @Component public class RateLimitAspect { @Autowired private StringRedisTemplate redisTemplate; @Around(@annotation(rateLimit)) public Object around(ProceedingJoinPoint pjp, RateLimit rateLimit) { String limitKey = 生成限流Key(pjp, rateLimit); int time = rateLimit.time(); int count = rateLimit.count(); if (isLimited(limitKey, time, count)) { throw new RuntimeException(rateLimit.message()); } return pjp.proceed(); } private boolean isLimited(String key, int time, int count) { Long currentCount = redisTemplate.opsForValue().increment(key); if (currentCount == 1) redisTemplate.expire(key, time, TimeUnit.SECONDS); return currentCount > count; } }
1.3 使用示例
@RestController public class UserController { @RateLimit(time = 60, count = 3) @GetMapping(/api/user/{id}) public User getUser(@PathVariable Long id) { /* ... */ } @RateLimit(key = #id + _ + #request.remoteAddr) @PostMapping(/api/user/{id}/update) public Result updateUser(...) { /* ... */ } }
优缺点:
- 优点:实现简单、无侵入性、支持接口粒度控制。
- 缺点:逻辑简单、无预警机制,分布式需依赖Redis。
2. 令牌桶算法实现限流
核心思路:基于Guava的令牌桶算法,允许突发流量但限制平均速率。
实现步骤
2.1 引入Guava依赖
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>31.1-jre</version> </dependency>
2.2 创建令牌桶管理器
@Component public class RateLimiter { private final ConcurrentHashMap<String, com.google.common.util.concurrent.RateLimiter> rateLimiterMap = new ConcurrentHashMap<>(); public boolean tryAcquire(String key, double permitsPerSecond) { return rateLimiterMap.computeIfAbsent(key, k -> RateLimiter.create(permitsPerSecond)).tryAcquire(); } }
2.3 实现拦截器
@Component public class TokenBucketInterceptor implements HandlerInterceptor { @Autowired private RateLimiter rateLimiter; @Override public boolean preHandle(HttpServletRequest request, ...) { String key = ip + requestURI; if (!rateLimiter.tryAcquire(key, 2.0)) { response.setStatus(429); response.getWriter().write({code:429,message:请求过于频繁}); return false; } return true; } }
优缺点:
- 优点:支持突发流量、单机性能好、配置灵活。
- 缺点:仅适用于单机、分布式需改造、状态易丢失。
3. 分布式限流(Redis + Lua脚本)
核心思路:利用Redis存储请求记录,通过Lua脚本实现原子性计数和滑动窗口。
实现步骤
3.1 定义Lua脚本rate_limiter.lua
local key = KEYS[1] local window = tonumber(ARGV[1]) local threshold = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) redis.call('ZREMRANGEBYSCORE', key, 0, now - window * 1000) local count = redis.call('ZCARD', key) if count >= threshold then return 0 end redis.call('ZADD', key, now, now..'-'..math.random()) redis.call('EXPIRE', key, window) return threshold - count - 1
3.2 创建Redis限流服务
@Service public class RedisRateLimiterService { @Autowired private StringRedisTemplate redisTemplate; private DefaultRedisScript<Long> rateLimiterScript; public long isAllowed(String key, int window, int threshold) { return redisTemplate.execute(rateLimiterScript, Collections.singletonList(key), String.valueOf(window), String.valueOf(threshold), String.valueOf(System.currentTimeMillis())); } }
3.3 使用示例
@DistributedRateLimit(mode = user, threshold = 5) @PostMapping(/api/payment) public Result createPayment(...) { /* ... */ }
优缺点:
- 优点:支持分布式、精确计数、原子性强。
- 缺点:依赖Redis、实现复杂、需维护Lua脚本。
4. 集成Sentinel实现接口防刷
核心思路:使用阿里巴巴Sentinel组件,提供QPS限流、熔断、热点参数防护等功能。
实现步骤
4.1 添加依赖
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
4.2 配置流控规则
@Configuration public class SentinelConfig { @PostConstruct public void initFlowRules() { FlowRule rule = new FlowRule(); rule.setResource(/api/user); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(10); // 每秒10请求 FlowRuleManager.loadRules(Arrays.asList(rule)); } }
4.3 异常处理
@RestControllerAdvice public class SentinelExceptionHandler { @ExceptionHandler(BlockException.class) public Result handleBlockException(BlockException e) { return Result.error(429, 请求过于频繁,请稍后再试); } }
优缺点:
- 优点:功能全面、支持动态规则、可视化管理。
- 缺点:学习成本高、分布式需配置规则持久化。
5. 验证码与行为分析防刷
核心思路:对敏感操作(如登录)要求验证码,并通过行为分析识别机器人。
实现步骤
5.1 生成与验证验证码
@Service public class CaptchaService { public String generateCaptcha(...) { SpecCaptcha captcha = new SpecCaptcha(130, 48, 5); redisTemplate.opsForValue().set(captchaId, captcha.text(), 5, TimeUnit.MINUTES); return captcha.toBase64(); } public boolean validateCaptcha(...) { String correctCode = redisTemplate.opsForValue().get(captchaId); return correctCode != null && correctCode.equals(captchaCode); } }
5.2 行为分析拦截器
@Component public class BehaviorAnalysisInterceptor { @Override public boolean preHandle(HttpServletRequest request, ...) { String ip = getIpAddress(request); Long count = redisTemplate.opsForValue().increment(behavior:freq:+ip); if (count > 30) { response.setStatus(429); return false; } return true; } }
优缺点:
- 优点:有效区分人类与机器人、防护强度高。
- 缺点:增加用户成本、可能被OCR破解、需前后端配合。
方案对比与选择
方案 | 实现难度 | 防刷效果 | 分布式支持 | 用户体验 | 适用场景 |
---|---|---|---|---|---|
基于注解的访问频率限制 | 低 | 中 | 需Redis | 一般 | 简单接口、单机场景 |
令牌桶算法 | 中 | 中高 | 单机 | 好 | 允许突发流量的单机服务 |
分布式限流(Redis+Lua) | 高 | 高 | 支持 | 一般 | 分布式系统、精确限流 |
集成Sentinel | 中高 | 高 | 需配置 | 可配置 | 复杂系统、多维度防护 |
验证码与行为分析 | 高 | 高 | 支持 | 较差 | 敏感操作、关键业务 |
总结
接口防刷需平衡安全性与用户体验,建议:
- 简单场景:使用基于注解的限流或令牌桶算法。
- 分布式系统:选择Redis+Lua或Sentinel。
- 敏感操作:结合验证码与行为分析增强防护。
实施时应遵循最小影响、梯度防护原则,并确保监控与动态调整能力。