# 限流与防刷

URL: https://caijiao.org/koa/advanced/ratelimit
Source: docs/koa/advanced/ratelimit.md
Description: 限流与防刷技术能有效保护 Koa 应用免受恶意请求和滥用。本文介绍了如何在 Koa 中实现限流与防刷机制，以保障系统的稳定性与安全性。

限流与防刷是防止应用遭受恶意请求（如 DDoS 攻击、暴力破解等）以及保护系统资源的一种常见技术。[Koa](/koa/) 提供了灵活的中间件支持，可以有效地实现限流与防刷功能。本文将介绍在 Koa 中实现限流与防刷的常见方法。

## 1. 限流原理

限流的目的是限制单位时间内某个用户或客户端的请求次数，从而防止恶意请求对服务器造成过大的压力。常见的限流策略包括：

- **固定窗口限流**：对请求进行时间窗口划分，限制每个时间窗口内的最大请求次数。
- **滑动窗口限流**：与固定窗口限流类似，但它更加动态，可以根据滑动时间窗口调整请求限制。
- **漏斗限流**：以固定速率将请求按队列处理，当队列满时，新的请求将被拒绝。

## 2. 使用 `koa-rate-limit` 实现限流

`koa-rate-limit` 是一个简单的限流中间件，它基于 IP 地址对请求进行限流。你可以使用它来实现常见的限流策略。

### 安装

```bash
npm install koa-rate-limit
````

### 使用示例

```js
const Koa = require('koa');
const rateLimit = require('koa-rate-limit');
const app = new Koa();

app.use(rateLimit({
  driver: 'memory',  // 使用内存作为存储驱动
  db: new Map(),     // 使用 Map 存储请求信息
  duration: 60000,    // 限制时间窗口，单位为毫秒
  errorMessage: 'Too many requests, please try again later.',  // 超过限制时返回的错误消息
  max: 100,           // 每分钟最多允许 100 次请求
}));

app.use(ctx => {
  ctx.body = 'Hello, world!';
});

app.listen(3000);
```

在这个例子中，每个 IP 地址每分钟最多可以请求 100 次。超过请求次数时，系统会返回 `"Too many requests, please try again later."` 错误消息。

## 3. 基于 Redis 的限流

对于高并发的应用，使用内存存储（如 `Map`）可能不够稳定，可以使用 Redis 作为存储后端，利用 Redis 的高性能来存储请求计数。

### 使用 `ioredis` 和 `koa-redis` 实现 Redis 限流

```bash
npm install ioredis koa-redis koa-rate-limit
```

### 使用示例

```js
const Koa = require('koa');
const Redis = require('ioredis');
const rateLimit = require('koa-rate-limit');
const redis = new Redis();
const app = new Koa();

app.use(rateLimit({
  driver: 'redis',
  db: redis,
  duration: 60000,
  errorMessage: 'Too many requests, please try again later.',
  max: 100,
}));

app.use(ctx => {
  ctx.body = 'Hello, world!';
});

app.listen(3000);
```

在这个例子中，Redis 用于存储请求计数，每分钟每个 IP 地址最多可以发起 100 次请求。

## 4. 防刷机制

防刷是指通过检测请求行为，防止恶意用户通过暴力破解或自动化工具大量刷请求。常见的防刷措施包括：

- **验证码**：对疑似恶意请求进行验证码验证。
- **请求频率限制**：对某些高频次请求（如登录请求）进行限制。
- **IP 黑名单**：对恶意 IP 地址进行封禁。

### 示例：登录请求防刷

对登录请求进行限流，防止暴力破解。

```js
const Koa = require('koa');
const rateLimit = require('koa-rate-limit');
const app = new Koa();

app.use(rateLimit({
  driver: 'memory',
  db: new Map(),
  duration: 300000,  // 5 分钟
  errorMessage: 'Too many login attempts. Please try again later.',
  max: 5,            // 每 5 分钟最多允许 5 次登录请求
}));

app.use(async (ctx) => {
  if (ctx.url === '/login' && ctx.method === 'POST') {
    // 执行登录逻辑
  }
  ctx.body = 'Login Page';
});

app.listen(3000);
```

在此示例中，每个 IP 地址每 5 分钟只能进行 5 次登录请求，超过限制会返回错误消息。

## 5. 滑动窗口限流

滑动窗口限流是一种更灵活的限流方式，它不会因为一个请求的突发到来而打破整个时间窗口。可以通过滑动时间窗的方式来平滑处理请求。

### 示例：滑动窗口实现

```js
const Koa = require('koa');
const rateLimit = require('koa-rate-limit');
const app = new Koa();

app.use(rateLimit({
  driver: 'memory',
  db: new Map(),
  duration: 60000,      // 每 1 分钟内
  errorMessage: 'Too many requests, please try again later.',
  max: 10,             // 限制每分钟 10 次请求
  disableHeader: true,  // 禁用响应头中的 X-RateLimit 信息
  windowMs: 60000,      // 滑动窗口的时间长度
  skipSuccessfulRequests: true, // 可以跳过成功的请求
}));

app.use(async (ctx) => {
  ctx.body = 'Rate-limited Request';
});

app.listen(3000);
```

滑动窗口限流的优势在于，它不会因为某一个请求突如其来的大量请求而导致请求过多，而是通过动态滑动窗口来处理。

## 总结

通过限流与防刷机制，我们可以有效防止恶意请求对应用造成过大压力，保护系统免受滥用和攻击。`koa-rate-limit` 是一个简单易用的限流中间件，结合 Redis 等存储方案，可以实现高效的限流策略。而防刷机制，如验证码和请求频率限制，能有效防止暴力破解等恶意行为，提高应用的安全性。
