【架构实战】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. 做好监控和告警

个人观点,仅供参考

相关推荐
明如正午11 小时前
转换pdf文件为md文件【markitdown+pdf4llm】
python·pdf·markitdown·pdf4llm
智慧化智能化数字化方案11 小时前
智能制造——解读IBM装备制造业大数据驱动的企业架构优化与智能化转型规划方案【附全文阅读】
大数据·架构·制造·装备制造业全寿命周期质量管理·装备制造业智能工厂·装备制造集团scm·sap大型装备制造集团erp
eastyuxiao11 小时前
OpenClaw 全功能说明文档
开发语言·人工智能
咯哦哦哦哦11 小时前
Foundationpose环境配置【非conda--纯UV】(linux22.04+python3.10)
python·pip·uv
solicitous11 小时前
JAVA系统复习(基础语法-类、接口)
java·开发语言
techdashen11 小时前
四个解析器引发的混乱:Cloudflare 如何用 Rust 统一全栈 Cron 解析
开发语言·rust·状态模式
likerhood12 小时前
单例模式详细讲解(java)
java·开发语言·单例模式
小短腿的代码世界12 小时前
Qt量化策略编辑器深度解析:从DSL解析到可视化编排的完整架构
qt·架构·编辑器
AC赳赳老秦12 小时前
项目闭环管理:用 OpenClaw 对接 Jira / 禅道,实现需求 - 任务 - 进度 - 验收全流程自动化
运维·人工智能·python·自动化·devops·jira·openclaw
穿越临界点12 小时前
有限状态机(FSM)
架构·状态机·决策