225 lines
6.4 KiB
Markdown
225 lines
6.4 KiB
Markdown
|
|
# 限流功能使用说明
|
|||
|
|
|
|||
|
|
## 功能概述
|
|||
|
|
|
|||
|
|
本项目已集成基于 Redis 滑动窗口算法的限流功能,支持两种使用方式:
|
|||
|
|
1. **配置化限流** - 在 `application.yml` 中配置规则,自动对所有接口生效
|
|||
|
|
2. **注解限流** - 在 Controller 方法上使用 `@RateLimit` 注解,针对特定接口限流
|
|||
|
|
|
|||
|
|
## 技术实现
|
|||
|
|
|
|||
|
|
### 核心组件
|
|||
|
|
|
|||
|
|
| 组件 | 说明 |
|
|||
|
|
|------|------|
|
|||
|
|
| `@RateLimit` | 限流注解,可自定义时间窗口、最大请求数 |
|
|||
|
|
| `RateLimitProperties` | 配置化限流的属性类 |
|
|||
|
|
| `RateLimiter` | Redis 限流工具类(滑动窗口算法) |
|
|||
|
|
| `RateLimitAspect` | 注解限流的 AOP 切面 |
|
|||
|
|
| `RateLimitInterceptor` | 配置化限流的拦截器 |
|
|||
|
|
| `ErrorCode.RATE_LIMIT_EXCEEDED` | 限流错误码(5001) |
|
|||
|
|
|
|||
|
|
### 限流优先级
|
|||
|
|
|
|||
|
|
**注解限流 > 配置化限流 > 默认限流**
|
|||
|
|
|
|||
|
|
## 使用方式
|
|||
|
|
|
|||
|
|
### 方式一:配置化限流(推荐作为基础防护)
|
|||
|
|
|
|||
|
|
在 `application.yml` 中配置:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
rate-limit:
|
|||
|
|
enabled: true # 是否启用限流
|
|||
|
|
default-time-window: 60 # 默认时间窗口(秒)
|
|||
|
|
default-max-requests: 1000 # 默认时间窗口内最大请求数
|
|||
|
|
rules: # 限流规则列表
|
|||
|
|
# 登录接口限流 - 防止暴力破解
|
|||
|
|
- pattern: "/api/v1/auth/login"
|
|||
|
|
time-window: 60
|
|||
|
|
max-requests: 10
|
|||
|
|
# 验证码接口限流 - 防止刷验证码
|
|||
|
|
- pattern: "/api/v1/auth/captcha"
|
|||
|
|
time-window: 60
|
|||
|
|
max-requests: 5
|
|||
|
|
# 短信接口限流 - 防止短信轰炸
|
|||
|
|
- pattern: "/api/v1/sms/**"
|
|||
|
|
time-window: 60
|
|||
|
|
max-requests: 3
|
|||
|
|
# 文件上传接口限流
|
|||
|
|
- pattern: "/api/v1/**/upload/**"
|
|||
|
|
time-window: 60
|
|||
|
|
max-requests: 30
|
|||
|
|
exclude-patterns: # 排除限流的接口
|
|||
|
|
- "/doc.html"
|
|||
|
|
- "/swagger-resources/**"
|
|||
|
|
- "/v3/api-docs/**"
|
|||
|
|
- "/webjars/**"
|
|||
|
|
- "/uploads/**"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 方式二:注解限流(针对特殊接口)
|
|||
|
|
|
|||
|
|
在 Controller 方法上添加 `@RateLimit` 注解:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
package com.reading.platform.controller.auth;
|
|||
|
|
|
|||
|
|
import com.reading.platform.common.annotation.RateLimit;
|
|||
|
|
import com.reading.platform.common.response.Result;
|
|||
|
|
import org.springframework.web.bind.annotation.*;
|
|||
|
|
|
|||
|
|
@RestController
|
|||
|
|
@RequestMapping("/api/v1/auth")
|
|||
|
|
public class AuthController {
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 登录接口 - 限流防止暴力破解
|
|||
|
|
* 60 秒内最多 10 次请求
|
|||
|
|
*/
|
|||
|
|
@PostMapping("/login")
|
|||
|
|
@RateLimit(time = 60, maxRequests = 10, message = "登录过于频繁,请稍后再试")
|
|||
|
|
public Result<LoginResponse> login(@RequestBody LoginRequest request) {
|
|||
|
|
// ... 登录逻辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 发送短信验证码 - 严格限流
|
|||
|
|
* 60 秒内最多 3 次请求
|
|||
|
|
*/
|
|||
|
|
@PostMapping("/captcha")
|
|||
|
|
@RateLimit(time = 60, maxRequests = 3, message = "操作过于频繁,请稍后再试")
|
|||
|
|
public Result<Void> sendCaptcha(@RequestParam String phone) {
|
|||
|
|
// ... 发送验证码逻辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 自定义限流键前缀
|
|||
|
|
* 适用于需要按用户 ID 等特殊维度限流的场景
|
|||
|
|
*/
|
|||
|
|
@PostMapping("/special")
|
|||
|
|
@RateLimit(keyPrefix = "special_api:", time = 60, maxRequests = 5)
|
|||
|
|
public Result<Void> specialApi() {
|
|||
|
|
// ... 特殊接口逻辑
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 常见限流场景建议值
|
|||
|
|
|
|||
|
|
| 接口类型 | 时间窗口 | 最大请求数 | 说明 |
|
|||
|
|
|---------|---------|-----------|------|
|
|||
|
|
| 登录接口 | 60 秒 | 10 | 防止暴力破解 |
|
|||
|
|
| 验证码/短信 | 60 秒 | 3-5 | 防止短信轰炸 |
|
|||
|
|
| 文件上传 | 60 秒 | 30 | 防止资源滥用 |
|
|||
|
|
| 普通业务接口 | 60 秒 | 100-1000 | 根据业务调整 |
|
|||
|
|
| 导出接口 | 60 秒 | 5-10 | 防止服务器过载 |
|
|||
|
|
|
|||
|
|
## 限流响应
|
|||
|
|
|
|||
|
|
当请求被限流拦截时,返回:
|
|||
|
|
|
|||
|
|
**配置化限流响应:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 5001,
|
|||
|
|
"message": "请求过于频繁,请稍后再试",
|
|||
|
|
"data": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
HTTP 状态码:`429 Too Many Requests`
|
|||
|
|
|
|||
|
|
响应头:
|
|||
|
|
```
|
|||
|
|
X-RateLimit-Limit: 1
|
|||
|
|
X-RateLimit-Remaining: 0
|
|||
|
|
Retry-After: 60
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**注解限流响应:**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"code": 5001,
|
|||
|
|
"message": "登录过于频繁,请稍后再试",
|
|||
|
|
"data": null
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Redis 数据结构
|
|||
|
|
|
|||
|
|
限流使用 Redis 的 `Sorted Set`(有序集合)存储:
|
|||
|
|
|
|||
|
|
- **Key 格式**: `rate_limit:{key}`
|
|||
|
|
- **Member**: 时间戳(毫秒)
|
|||
|
|
- **Score**: 时间戳(毫秒)
|
|||
|
|
|
|||
|
|
示例:
|
|||
|
|
```
|
|||
|
|
rate_limit:192.168.1.1:/api/v1/auth/login
|
|||
|
|
|- 1710000000000
|
|||
|
|
|- 1710000001000
|
|||
|
|
|- 1710000002000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **Redis 依赖**: 限流功能依赖 Redis,需确保 Redis 服务可用
|
|||
|
|
2. **异常情况放行**: 当 Redis 不可用时,系统会自动放行请求,避免影响正常业务
|
|||
|
|
3. **集群部署**: 滑动窗口算法天然支持分布式,无需额外配置
|
|||
|
|
4. **监控告警**: 建议添加限流触发的监控告警,及时发现异常流量
|
|||
|
|
|
|||
|
|
## 日志示例
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
2024-03-10 22:00:00 WARN - 请求被限流拦截:URI=/api/v1/auth/login, 规则=/api/v1/auth/login, 最大请求数=10
|
|||
|
|
2024-03-10 22:00:01 DEBUG - 请求通过限流检查:URI=/api/v1/users, 规则=default
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 测试方法
|
|||
|
|
|
|||
|
|
使用 `curl` 或 Postman 快速测试:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 循环发送 15 次登录请求(第 11 次开始应该被限流)
|
|||
|
|
for i in {1..15}; do
|
|||
|
|
echo "请求 $i:"
|
|||
|
|
curl -X POST http://localhost:8080/api/v1/auth/login \
|
|||
|
|
-H "Content-Type: application/json" \
|
|||
|
|
-d '{"username":"test","password":"test"}' \
|
|||
|
|
-w "\nHTTP 状态码:%{http_code}\n\n"
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 配置说明
|
|||
|
|
|
|||
|
|
### RateLimit 注解参数
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 默认值 | 说明 |
|
|||
|
|
|------|------|--------|------|
|
|||
|
|
| `keyPrefix` | String | "" | 限流键前缀,默认使用 `IP:类名。方法名` |
|
|||
|
|
| `time` | long | 60 | 时间窗口大小 |
|
|||
|
|
| `timeUnit` | TimeUnit | SECONDS | 时间单位 |
|
|||
|
|
| `maxRequests` | long | 100 | 时间窗口内最大请求数 |
|
|||
|
|
| `message` | String | "请求过于频繁,请稍后再试" | 限流提示信息 |
|
|||
|
|
| `enabled` | boolean | true | 是否启用限流 |
|
|||
|
|
|
|||
|
|
### application.yml 配置参数
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 默认值 | 说明 |
|
|||
|
|
|------|------|--------|------|
|
|||
|
|
| `enabled` | boolean | true | 是否启用限流 |
|
|||
|
|
| `default-time-window` | long | 60 | 默认时间窗口(秒) |
|
|||
|
|
| `default-max-requests` | long | 1000 | 默认最大请求数 |
|
|||
|
|
| `rules` | List | [] | 限流规则列表 |
|
|||
|
|
| `exclude-patterns` | List | [] | 排除限流的接口路径 |
|
|||
|
|
|
|||
|
|
### RateLimitRule 配置参数
|
|||
|
|
|
|||
|
|
| 参数 | 类型 | 说明 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `pattern` | String | 接口路径(支持 Ant 风格通配符) |
|
|||
|
|
| `time-window` | long | 时间窗口(秒) |
|
|||
|
|
| `max-requests` | long | 最大请求数 |
|