【架构实战】API接口防刷与限流策略

一、接口防刷概述

接口防刷是保护系统安全的重要手段:

常见攻击:

  • 暴力破解密码
  • 恶意爬虫
  • 刷接口(抽奖、秒杀)
  • CC攻击

二、限流算法

1. 计数器算法

java 复制代码
@Component
public class CounterRateLimiter {
    
    public boolean tryAcquire(String key, int limit, int window) {
        String redisKey = "ratelimit:" + key;
        Long count = redisTemplate.opsForValue().increment(redisKey);
        
        if (count == 1) {
            redisTemplate.expire(redisKey, window, TimeUnit.SECONDS);
        }
        
        return count <= limit;
    }
}

2. 滑动窗口算法

java 复制代码
@Component
public class SlidingWindowRateLimiter {
    
    public boolean tryAcquire(String key, int limit, int window) {
        long now = System.currentTimeMillis();
        String redisKey = "ratelimit:sliding:" + key;
        
        // 移除窗口外的请求
        redisTemplate.opsForZSet().removeRangeByScore(
            redisKey, 0, now - window * 1000);
        
        // 当前请求数
        Long count = redisTemplate.opsForZSet().zCard(redisKey);
        
        if (count >= limit) {
            return false;
        }
        
        // 添加当前请求
        redisTemplate.opsForZSet().add(redisKey, now, now);
        redisTemplate.expire(redisKey, window * 2, TimeUnit.SECONDS);
        
        return true;
    }
}

3. 令牌桶算法

java 复制代码
@Component
public class TokenBucketRateLimiter {
    
    public boolean tryAcquire(String key, int bucketSize, int refillRate) {
        String redisKey = "tokenbucket:" + key;
        
        // 获取令牌
        Long tokens = redisTemplate.opsForValue().decrement(redisKey + ":tokens");
        
        if (tokens == null) {
            // 初始化令牌桶
            redisTemplate.opsForValue().set(redisKey + ":tokens", bucketSize - 1);
            redisTemplate.opsForValue().set(redisKey + ":last", System.currentTimeMillis());
            return true;
        }
        
        if (tokens < 0) {
            // 令牌不足
            return false;
        }
        
        return true;
    }
}

三、分布式限流

1. Lua脚本限流

java 复制代码
@Component
public class LuaRateLimiter {
    
    private static final String SCRIPT = """
        local key = KEYS[1]
        local limit = tonumber(ARGV[1])
        local window = tonumber(ARGV[2])
        
        local current = redis.call('INCR', key)
        
        if current == 1 then
            redis.call('EXPIRE', key, window)
        end
        
        if current > limit then
            return 0
        end
        
        return 1
        """;
    
    public boolean tryAcquire(String key, int limit, int window) {
        DefaultRedisScript<Long> script = new DefaultScript();
        script.setScriptText(SCRIPT);
        
        Long result = redisTemplate.execute(
            script,
            Collections.singletonList(key),
            String.valueOf(limit),
            String.valueOf(window)
        );
        
        return result != null && result == 1;
    }
}

2. Sentinel限流

java 复制代码
@Service
public class SentinelRateLimiter {
    
    @SentinelResource(value = "getOrder", blockHandler = "blockHandler")
    public Order getOrder(Long orderId) {
        return orderService.getById(orderId);
    }
    
    public Order blockHandler(Long orderId, BlockException e) {
        throw new BusinessException("请求过于频繁,请稍后重试");
    }
}

// 配置限流规则
@Configuration
public class SentinelConfig {
    
    @PostConstruct
    public void initRules() {
        List<FlowRule> rules = new ArrayList<>();
        
        FlowRule rule = new FlowRule();
        rule.setResource("getOrder");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(100);  // 每秒100次
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        
        rules.add(rule);
        
        FlowRuleManager.loadRules(rules);
    }
}

四、接口防刷策略

1. 签名验证

java 复制代码
@Component
public class SignValidator {
    
    @Value("${app.secret}")
    private String appSecret;
    
    public boolean validateSign(Map<String, String> params, String sign) {
        // 1. 排序参数
        String sortedParams = params.entrySet().stream()
            .filter(e -> !e.getKey().equals("sign"))
            .sorted(Map.Entry.comparingByKey())
            .map(e -> e.getKey() + "=" + e.getValue())
            .collect(Collectors.joining("&"));
        
        // 2. 拼接密钥
        String signStr = sortedParams + "&key=" + appSecret;
        
        // 3. MD5签名
        String calculatedSign = MD5(signStr).toUpperCase();
        
        return calculatedSign.equals(sign.toUpperCase());
    }
}

2. 时间戳防重放

java 复制代码
@Component
public class TimestampValidator {
    
    private static final long MAX_TIMESTAMP_DIFF = 300000; // 5分钟
    
    public boolean validateTimestamp(long timestamp) {
        long now = System.currentTimeMillis();
        long diff = Math.abs(now - timestamp);
        return diff <= MAX_TIMESTAMP_DIFF;
    }
}

3. 唯一请求ID

java 复制代码
@Component
public class IdempotentChecker {
    
    public boolean check(String requestId) {
        String key = "idempotent:" + requestId;
        Boolean result = redisTemplate.opsForValue()
            .setIfAbsent(key, "1", 5, TimeUnit.MINUTES);
        
        return Boolean.TRUE.equals(result);
    }
}

五、验证码防护

1. 图形验证码

java 复制代码
@Service
public class CaptchaService {
    
    public Captcha generateCaptcha() {
        // 生成随机字符串
        String code = generateRandomCode(4);
        
        // 生成图片
        BufferedImage image = generateImage(code);
        
        // 生成唯一ID
        String captchaId = UUID.randomUUID().toString();
        
        // 存入Redis
        redisTemplate.opsForValue().set(
            "captcha:" + captchaId,
            code.toLowerCase(),
            5,
            TimeUnit.MINUTES
        );
        
        return new Captcha(captchaId, image);
    }
    
    public boolean verify(String captchaId, String code) {
        String cachedCode = (String) redisTemplate.opsForValue()
            .get("captcha:" + captchaId);
        
        if (cachedCode == null) {
            return false;
        }
        
        boolean valid = cachedCode.equalsIgnoreCase(code);
        
        if (valid) {
            redisTemplate.delete("captcha:" + captchaId);
        }
        
        return valid;
    }
}

2. 短信验证码

java 复制代码
@Service
public class SmsCodeService {
    
    public void sendSmsCode(String phone) {
        // 检查发送频率
        String sendKey = "sms:send:" + phone;
        Long sendCount = redisTemplate.opsForValue().increment(sendKey);
        
        if (sendCount != null && sendCount > 5) {
            throw new BusinessException("发送过于频繁,请1小时后再试");
        }
        
        if (sendCount == 1) {
            redisTemplate.expire(sendKey, 1, TimeUnit.HOURS);
        }
        
        // 生成6位验证码
        String code = String.format("%06d", new Random().nextInt(1000000));
        
        // 存入Redis
        String codeKey = "sms:code:" + phone;
        redisTemplate.opsForValue().set(codeKey, code, 5, TimeUnit.MINUTES);
        
        // 发送短信
        smsClient.send(phone, "验证码:" + code);
    }
}

六、IP限流

java 复制代码
@Component
public class IPRateLimiter {
    
    public boolean tryAcquire(String ip, String api, int limit, int window) {
        String key = "ip:ratelimit:" + ip + ":" + api;
        
        Long count = redisTemplate.opsForValue().increment(key);
        
        if (count == 1) {
            redisTemplate.expire(key, window, TimeUnit.SECONDS);
        }
        
        if (count > limit) {
            // 封禁IP
            blockIP(ip);
            return false;
        }
        
        return true;
    }
    
    private void blockIP(String ip) {
        String blockKey = "ip:block:" + ip;
        redisTemplate.opsForValue().set(blockKey, "1", 1, TimeUnit.HOURS);
    }
}

七、总结

接口防刷是系统安全的重要组成:

  • 限流算法:计数器、滑动窗口、令牌桶
  • 分布式限流:Lua脚本、Sentinel
  • 签名验证:防止请求被篡改
  • 验证码:防止机器攻击

最佳实践:

  1. 多层限流(网关+应用+数据库)
  2. 根据业务场景选择限流策略
  3. 做好监控和告警

个人观点,仅供参考

相关推荐
人道领域2 小时前
2026年Java后端热点全景解析:从LTS革新到云原生跃迁
java·开发语言
188号安全攻城狮2 小时前
【前端基础知识】JavaScript 数组方法总结:从表格速查到分类详解
开发语言·前端·javascript·网络安全
不爱吃炸鸡柳2 小时前
5道经典贪心算法题详解:从入门到进阶
开发语言·数据结构·c++·算法·贪心算法
qq_381338502 小时前
微前端架构深度实践:从 qiankun 到 Module Federation 的企业级方案
前端·架构
zandy10112 小时前
业界首发|衡石科技HENGSHI CLI重磅登场,以Rust架构开启Agentic BI自动驾驶时代
科技·架构·rust·agentic bi
xyq20242 小时前
Java 变量命名规则
开发语言
天启HTTP2 小时前
HTTP代理和隧道代理的底层区别与适用场景分析
开发语言·网络协议·tcp/ip·php
小白学大数据2 小时前
告别复杂 XPath:DeepSeek+Python 爬虫快速实践
开发语言·爬虫·python·selenium
147API2 小时前
Claude、GPT、Gemini 场景分工实战:模型路由架构指南
架构·api·多模型协同·api大模型