砍材农夫砍材农夫
  • 微信记账小程序
  • java
  • redis
  • mysql
  • 场景类
  • 框架类
  • vuepress搭建
  • hexo搭建
  • 云图
  • 常用工具

    • git
    • gradle
    • Zadig
    • it-tools
    • 开源推荐
    • curl
  • 大前端

    • nodejs
    • npm
    • webpack
    • 微信
    • 正则
    • uniapp
  • java

    • java基础
    • jdk体系
    • jvm
    • spring
    • spring_cloud
    • spring_boot
    • 分库分表
    • zookeeper
  • python

    • python基础
    • python高级
    • python框架
  • 算法

    • 算法
  • 网关

    • spring_cloud_gateway
    • openresty
  • 高可用

    • 秒杀
    • 分布式
    • 缓存一致
  • MQ

    • MQ
    • rabbitMQ
    • rocketMQ
    • kafka
  • 其它

    • 设计模式
    • 领域驱动(ddd)
  • 关系型数据库

    • mysql5.0
    • mysql8.0
  • 非关系型数据库

    • redis
    • mongoDB
  • 分布式/其他

    • ShardingSphere
    • 区块链
  • 向量数据库

    • M3E
    • OPEN AI
  • Jmeter
  • fiddler
  • wireshark
  • AI入门
  • AI大模型
  • AI插件
  • AI集成框架
  • 相关算法
  • AI训练师
  • 量化交易
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 相关运营
  • docker
  • k8s
  • devops
  • nginx
  • 元宇宙
  • 区块链
  • 物联网
  • linux
  • webrtc
  • web3.0
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 中考
  • 投资
  • 保险
  • 思
  • 微信记账小程序
  • java
  • redis
  • mysql
  • 场景类
  • 框架类
  • vuepress搭建
  • hexo搭建
  • 云图
  • 常用工具

    • git
    • gradle
    • Zadig
    • it-tools
    • 开源推荐
    • curl
  • 大前端

    • nodejs
    • npm
    • webpack
    • 微信
    • 正则
    • uniapp
  • java

    • java基础
    • jdk体系
    • jvm
    • spring
    • spring_cloud
    • spring_boot
    • 分库分表
    • zookeeper
  • python

    • python基础
    • python高级
    • python框架
  • 算法

    • 算法
  • 网关

    • spring_cloud_gateway
    • openresty
  • 高可用

    • 秒杀
    • 分布式
    • 缓存一致
  • MQ

    • MQ
    • rabbitMQ
    • rocketMQ
    • kafka
  • 其它

    • 设计模式
    • 领域驱动(ddd)
  • 关系型数据库

    • mysql5.0
    • mysql8.0
  • 非关系型数据库

    • redis
    • mongoDB
  • 分布式/其他

    • ShardingSphere
    • 区块链
  • 向量数据库

    • M3E
    • OPEN AI
  • Jmeter
  • fiddler
  • wireshark
  • AI入门
  • AI大模型
  • AI插件
  • AI集成框架
  • 相关算法
  • AI训练师
  • 量化交易
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 相关运营
  • docker
  • k8s
  • devops
  • nginx
  • 元宇宙
  • 区块链
  • 物联网
  • linux
  • webrtc
  • web3.0
  • gitee
  • github
  • infoq
  • osc
  • 砍材工具
  • 关于
  • 中考
  • 投资
  • 保险
  • 思
  • 常见案例分析

    • 分布式ID
    • 分布式锁
    • 订单超时取消
    • 库存扣减问题
    • 幂等接口设计
    • 接口限流设计
    • 接口防刷设计
    • 多级缓存设计
  • 相关拓展知识

    • TCP和UDP区别
    • 签名算法
    • 身份认证令牌
    • 如何防止class类被反编译
    • 消息队列
    • maven如何统一指定jdk
    • maven常用插件
  • 算法|数据结构

    • 二叉树
    • 红黑树
    • 快速比较两棵树查找相同节点方法
  • 主要目的
  • 存在的场景
  • 应对的策略
    • 请求合法性校验
    • 限流
    • 验证码机制
    • 黑/白名单
  • 总结

主要目的

保护系统资源、保障用户体验和确保业务安全

存在的场景

  • 爬虫

大量抓取数据,耗尽服务器资源

  • 暴力破解

针对登录接口高频尝试密码

  • CC攻击

应用层DDoS,耗尽业务资源

  • 业务滥用

如秒杀场景下的机器抢单

应对的策略

请求合法性校验

服务端处理请求之前,验证该请求是否来自可信的客户端、是否完整未被篡改、是否在有效期内的一系列措施。 它是接口防刷的第一道防线,用于拦截伪造、重放、篡改的恶意请求

  • API签名校验
// 客户端生成签名(伪代码)
String secret = "your-secret";
String data = "param1=value1&param2=value2";
long timestamp = System.currentTimeMillis();
String nonce = UUID.randomUUID().toString();
String sign = HmacSHA256(data + timestamp + nonce, secret);

// 服务端校验拦截器
public boolean preHandle(HttpServletRequest request) {
    String sign = request.getHeader("X-Sign");
    long timestamp = Long.parseLong(request.getHeader("X-Timestamp"));
    String nonce = request.getHeader("X-Nonce");
    String body = getRequestBody(request);
    
    // 1. 时间戳检查
    if (Math.abs(System.currentTimeMillis() - timestamp) > 5 * 60 * 1000) {
        return false; // 请求过期
    }
    
    // 2. Nonce检查(Redis)
    String nonceKey = "nonce:" + nonce;
    if (redisTemplate.hasKey(nonceKey)) {
        return false; // 重复请求
    }
    redisTemplate.opsForValue().set(nonceKey, "1", 5, TimeUnit.MINUTES);
    
    // 3. 重新计算签名
    String serverSign = HmacSHA256(body + timestamp + nonce, getSecretByAppKey(appKey));
    if (!serverSign.equals(sign)) {
        return false; // 签名错误
    }
    return true;
}
  • 身份认证(JWT校验示例)
@GetMapping("/user/info")
public Result getUserInfo(@RequestHeader("Authorization") String token) {
    try {
        // 解析JWT,验证签名和过期时间
        Claims claims = Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(token.replace("Bearer ", ""))
                .getBody();
        String userId = claims.getSubject();
        // 业务处理
    } catch (Exception e) {
        return Result.error(401, "无效token");
    }
}

限流

  • 基于redis demo
@Component
public class RateLimitInterceptor implements HandlerInterceptor {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 加载Lua脚本
    private DefaultRedisScript<Long> redisScript;
    
    @PostConstruct
    public void init() {
        redisScript = new DefaultRedisScript<>();
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("ratelimit.lua")));
        redisScript.setResultType(Long.class);
    }
    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ip = getClientIp(request);
        String key = "rate:ip:" + ip;
        long max = 10;        // 10次
        long period = 60;     // 60秒
        
        Long result = redisTemplate.execute(redisScript, Arrays.asList(key), String.valueOf(max), String.valueOf(period));
        if (result == null || result == 0) {
            response.setStatus(429);
            response.getWriter().write("Too Many Requests");
            return false;
        }
        return true;
    }
    
    private String getClientIp(HttpServletRequest request) {
        // 从x-forwarded-for等头获取真实IP
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty()) ip = request.getRemoteAddr();
        return ip;
    }
}

验证码机制

验证码(CAPTCHA)是“区分计算机和人类的公开全自动图灵测试”的缩写

  • 防止恶意程序自动提交表单(如注册、登录、评论)

  • 阻止暴力破解密码、刷票、爬虫等自动化攻击

  • 在触发限流策略后,作为二次验证手段,确认操作由真实用户发起

  • 配置Kaptcha(生成图形验证码)

@Configuration
public class KaptchaConfig {
    @Bean
    public Producer kaptchaProducer() {
        Properties properties = new Properties();
        properties.setProperty("kaptcha.image.width", "150");
        properties.setProperty("kaptcha.image.height", "50");
        properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ");
        properties.setProperty("kaptcha.textproducer.char.length", "4");
        properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        properties.setProperty("kaptcha.background.clear.from", "255,255,255");
        properties.setProperty("kaptcha.background.clear.to", "255,255,255");
        DefaultKaptcha kaptcha = new DefaultKaptcha();
        kaptcha.setConfig(new Config(properties));
        return kaptcha;
    }
}
  • 生成验证码接口(Controller)
@RestController
public class CaptchaController {
    @Autowired
    private Producer captchaProducer;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("/captcha")
    public void getCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 生成验证码文本
        String captchaText = captchaProducer.createText();
        // 生成验证码图片
        BufferedImage image = captchaProducer.createImage(captchaText);
        
        // 存储到Redis(使用唯一标识,如UUID或手机号)
        String captchaKey = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set("captcha:" + captchaKey, captchaText, 5, TimeUnit.MINUTES);
        
        // 将key返回给前端(可通过响应头或JSON)
        response.setHeader("Captcha-Key", captchaKey);
        
        // 输出图片
        response.setContentType("image/jpeg");
        ServletOutputStream out = response.getOutputStream();
        ImageIO.write(image, "jpg", out);
        out.close();
    }
}

黑/白名单

在接口防刷体系中,黑/白名单是一种基于来源(IP、用户ID、设备指纹等)的快速过滤机制

  • 名单格式

IP黑/白名单:支持单个IP、IP段(CIDR,如192.168.1.0/24)、通配符(如192.168..)

用户ID黑/白名单:针对已登录用户的UID进行控制

设备指纹:针对移动端设备ID(如IMEI、IDFA)进行封禁

@Component
public class BlackWhiteListInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    // IP工具:判断IP是否在某个CIDR网段内
    private boolean ipMatchesCidr(String ip, String cidr) {
        try {
            SubnetUtils subnet = new SubnetUtils(cidr);
            return subnet.getInfo().isInRange(ip);
        } catch (Exception e) {
            return false; // 格式错误视为不匹配
        }
    }

    // 检查是否在白名单中
    private boolean isInWhitelist(String ip) {
        Set<String> whitelist = redisTemplate.opsForSet().members("whitelist:ip");
        if (whitelist == null || whitelist.isEmpty()) return false;
        for (String pattern : whitelist) {
            if (pattern.contains("/")) { // CIDR网段
                if (ipMatchesCidr(ip, pattern)) return true;
            } else if (pattern.contains("*")) { // 通配符简单处理(如192.168.*.*)
                // 可将通配符转换为正则,此处简化
                String regex = pattern.replace(".", "\\.").replace("*", "\\d+");
                if (ip.matches(regex)) return true;
            } else { // 精确IP
                if (pattern.equals(ip)) return true;
            }
        }
        return false;
    }

    // 检查是否在黑名单中
    private boolean isInBlacklist(String ip) {
        // 直接判断Set中是否存在该IP(精确匹配),但IP段需要遍历判断
        Set<String> blacklist = redisTemplate.opsForSet().members("blacklist:ip");
        if (blacklist == null || blacklist.isEmpty()) return false;
        for (String pattern : blacklist) {
            if (pattern.contains("/")) {
                if (ipMatchesCidr(ip, pattern)) return true;
            } else if (pattern.contains("*")) {
                String regex = pattern.replace(".", "\\.").replace("*", "\\d+");
                if (ip.matches(regex)) return true;
            } else {
                if (pattern.equals(ip)) return true;
            }
        }
        return false;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String ip = getClientIp(request);

        // 1. 白名单检查:如果命中,直接放行
        if (isInWhitelist(ip)) {
            return true;
        }

        // 2. 黑名单检查:如果命中,拒绝请求
        if (isInBlacklist(ip)) {
            response.setStatus(403);
            response.getWriter().write("Your IP is blocked due to abnormal behavior.");
            return false;
        }

        // 3. 继续后续拦截器(如限流)
        return true;
    }

    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty()) ip = request.getRemoteAddr();
        // 处理多级代理,取第一个非unknown的IP
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }
}

总结

接口防刷是一个系统工程,需结合多种手段层层设防

  • 基础防御:签名、限流、黑白名单
  • 动态防御:验证码、行为分析
  • 兜底防御:异步队列、熔断降级
最近更新: 2026/3/3 16:05
Contributors: kcnf
Prev
接口限流设计
Next
多级缓存设计