背景
在微服务和开放平台中,接口安全 与防滥用是两个核心问题。
-
如果没有认证机制,恶意用户可以轻易伪造请求,造成数据泄露或业务风险。
-
如果没有限流机制,接口可能被爬虫或恶意程序高频调用,导致服务过载甚至宕机。
因此,常见的防护组合是:
-
API Key ------ 用于标识调用方身份,防止匿名或未授权调用。
-
滑动窗口限流 ------ 防止单个 API Key 或 IP 在短时间内请求过多,保证系统稳定性。
API Key 机制设计
1. API Key 的生成与分发
-
随机生成 :使用高强度的随机数(如
UUID + HMAC
)生成唯一 Key。 -
存储管理:保存到数据库或 Redis,并与调用方信息绑定(用户、应用、权限范围)。
-
下发方式:通过控制台、管理后台分配,开发者集成到调用请求头。
2. API Key 校验流程
-
客户端调用接口时,在 Header 中附带:
GET /api/orders Authorization: ApiKey xxx-yyy-zzz
-
服务端拦截器验证:
-
Key 是否存在?
-
Key 是否有效(未过期、未禁用)?
-
Key 是否具备调用该接口的权限?
-
-
校验通过 → 进入业务逻辑;否则直接返回 401 Unauthorized。
3. 代码示例(Java Spring Boot)
@Component
public class ApiKeyInterceptor implements HandlerInterceptor {
private static final String HEADER_NAME = "Authorization";
private final ApiKeyService apiKeyService;
public ApiKeyInterceptor(ApiKeyService apiKeyService) {
this.apiKeyService = apiKeyService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String header = request.getHeader(HEADER_NAME);
if (header == null || !header.startsWith("ApiKey ")) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing API Key");
return false;
}
String key = header.substring(7);
if (!apiKeyService.isValid(key)) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid API Key");
return false;
}
return true;
}
}
滑动窗口限流设计
1. 为什么选滑动窗口?
-
固定窗口(Fixed Window):简单,但在窗口边界处可能产生突刺流量。
-
漏桶(Leaky Bucket):稳定,但实现复杂。
-
令牌桶(Token Bucket):更灵活,但需要周期发放令牌。
-
滑动窗口(Sliding Window):兼顾实时性与精度,能更平滑地统计时间区间内的请求数。
2. 核心思想
-
以 Redis 或内存结构保存每个 API Key 的调用时间戳。
-
每次请求时:
-
移除窗口外的旧请求;
-
判断剩余请求数是否超过阈值;
-
若超过则拒绝请求,否则允许并记录本次调用。
-
3. Redis 实现示例(Java)
public boolean isAllowed(String apiKey) {
long now = System.currentTimeMillis();
long windowSize = 60_000; // 1分钟
int limit = 100; // 每分钟最多100次
String key = "rate_limit:" + apiKey;
try (Jedis jedis = jedisPool.getResource()) {
jedis.zremrangeByScore(key, 0, now - windowSize);
jedis.zadd(key, now, String.valueOf(now));
long count = jedis.zcard(key);
jedis.expire(key, 60); // 避免长期占用空间
return count <= limit;
}
}
性能表现
-
API Key 校验:查询 Redis/DB,复杂度 O(1),延迟 <1ms。可通过本地缓存 + 异步刷新进一步优化。
-
滑动窗口限流 :Redis 实现时间复杂度约 O(logN)(
ZSET
插入/删除/统计),在百万级 QPS 下表现稳定。 -
单机内存实现性能更高,但无法分布式扩展;推荐生产环境使用 Redis 或其他分布式 KV。
复杂度分析
-
实现复杂度:
-
API Key:低(生成 + 校验)。
-
滑动窗口:中(需要合理选择存储结构与过期策略)。
-
-
运维复杂度:
- 需要额外维护 Redis 集群,高可用时需加上监控与熔断。
-
调优点:
-
窗口大小与限流阈值需要根据接口 QPS 历史数据动态调整。
-
高并发下需要控制 Redis 连接池大小与超时设置。
-
常见问题
-
API Key 被泄露怎么办?
- 解决:支持后台快速吊销 / 重置;配合 IP 白名单、签名校验。
-
恶意用户绕过限流?
- 解决:多维度限流(按 API Key + IP + 用户 ID);引入验证码、行为识别。
-
分布式多节点下限流不一致?
- 解决:使用 Redis 统一存储限流数据,保证全局一致。
-
限流导致误伤正常用户?
- 解决:白名单机制,给部分高优先级 Key 更高额度。
典型应用案例
-
开放平台 API:
- 为每个开发者分配 API Key,配合滑动窗口限流,既能追踪调用来源,又能避免爬虫滥用。
-
支付接口:
- 防止恶意刷单,采用"API Key + 用户 ID 双维度"限流,并记录异常调用日志。
-
内部微服务调用:
- 在微服务网关层面统一校验 API Key(或 JWT),并对接口做限流保护,防止雪崩效应。
总结
-
API Key 认证 → 保证调用方身份合法,防止接口被匿名/恶意调用。
-
滑动窗口限流 → 在时间维度平滑限制调用频率,避免过载。
-
这两者结合,既保证了接口安全,又能提升系统稳定性,是微服务与开放平台接口防护的常用方案。