引言
接口防刷是生产项目落地必须解决的问题,这篇文章会从架构的角度,分层次的讲讲如何解决这个问题。
接口防刷(Rate Limiting / Anti-scraping)的核心在于"识别请求"和"限制频率"

第一层:客户端/前端层 (Client Side)
在client层我们并不能阻止真正的攻击者,属于"防君子不防小人",主要目的是增加作弊成本,而不是彻底阻断。
下面是在这一层常见的措施。
-
UI 交互限制
- 按钮置灰:点击发送后,按钮强制置灰几秒钟(防止用户手抖重复提交)。
-
图形/行为验证码 (CAPTCHA)
- 在敏感接口(注册、登录、领券)引入滑块、点击选字或 Google ReCaptcha。
- 逻辑:只有验证码校验通过,才颁发一个临时的 Token,后端接口校验该 Token。
-
参数签名 (Signature) & 防重放
- 机制:客户端使用 AK/SK 对请求参数 + 时间戳 + 随机数 (Nonce) 进行签名。
- 作用:防止抓包篡改参数。
- 防重放:后端校验时间戳(例如只允许 60s 内的请求),并缓存 Nonce(60s 内不能重复)。
第二层:网络/网关接入层 (Network / Gateway)
在这一层我们一定要挡住绝大部分的异常流量,保护后端服务不被压垮。
- WAF (Web Application Firewall)
- 如果公司有预算,直接上对应云服务厂商(比如阿里云/AWS) 的 WAF 或者 Cloudflare。它们能基于指纹库识别恶意爬虫、Bot 流量,直接在边缘节点阻断。
2.Nginx 反向代理层
-
IP 限流 (
limit_req_zone) :这是最基础的。nginx# 定义限流空间,以 IP 为 key,限制每秒 10 个请求 limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; server { location /api/ { # 应用限流,burst=5 允许瞬间突发 5 个,nodelay 立即处理 limit_req zone=api_limit burst=5 nodelay; } } -
黑名单机制 :Nginx 可以直接
deny某些恶意 IP 段。
- API 网关 (Gateway)
-
技术栈 :Spring Cloud Gateway/Zuul,Kong, APISIX , Tyk,Higress。
-
策略:
- 身份鉴权:在网关层校验 Token (JWT),无效请求直接丢弃,不透传给后端。
- 全局流控:基于令牌桶(Token Bucket)或漏桶(Leaky Bucket)算法,限制整个服务的 QPS 上限。
第三层:应用/服务层 (Application / Middleware)
这里就是业务层来阻断的地方了,可以针对某个业务进行更加精细的限流操作。
- 业务维度的限流 (Redis + Lua)
-
场景 :限制某个用户 (User_ID) 在特定时间窗口内只能调用 N 次。
-
为什么用 Lua? 保证 Redis 操作(读+写)的原子性。
-
滑动窗口算法:比固定窗口更平滑,防止"临界点突发"问题。
- 示例逻辑 :使用 Redis
ZSET。Key 是User_ID:Action,Score 是时间戳。每次请求移除窗口外的数据,统计窗口内的数量。
- 示例逻辑 :使用 Redis
- 单机/集群限流组件
-
Java 生态:
- Guava RateLimiter:单机限流,基于令牌桶,适合非集群环境。
- Alibaba Sentinel:神器。支持单机、集群、热点参数限流(例如:防止某个热点商品 ID 被刷),支持降级熔断。
-
Go 生态:
golang.org/x/time/rate:官方标准库,基于令牌桶。- Uber-go/ratelimit:基于漏桶模型,更注重请求的均匀性。
- 幂等性设计 (Idempotency)
- 防止因为网络抖动或脚本重试导致的重复操作。
- 实现 :客户端生成唯一的
Request-ID,后端在拦截器和Redis中检查该 ID 是否已处理过。
如果面对脚本,我们在这一层一般有什么解决方法呢?
-
动态风控/黑名单
- 分析用户行为模型。如果一个用户 24 小时都在请求接口,或者只抢红包不看页面,标记为灰名单/黑名单。
- Java/Go 处理:在 Filter/Middleware 中,检查用户 ID 是否在 Redis 的黑名单 Set 中,如果在,直接返回 403。
-
人机验证升级
- 当系统检测到某用户频率稍高但不确定是否为攻击时,不直接封禁,而是弹出验证码。
- 只有通过验证码,才允许继续操作。
第四层:数据持久层 (Database)
最后的兜底,防止数据错乱。
-
数据库唯一索引 (Unique Index)
- 例如:防止用户重复领取优惠券,在
coupon_record表对user_id+campaign_id建唯一索引。
- 例如:防止用户重复领取优惠券,在
-
悲观锁/乐观锁
- 乐观锁 :
UPDATE account SET balance = balance - 100, version = version + 1 WHERE id = 1 AND version = old_version。防止并发扣减刷成负数。
- 乐观锁 :
总结
ok,这就是接口防刷(限流)的分层操作,如果有错误或者笔误、或者有更好的建议,期待在评论区提出来,谢谢!