## 限速
为了保护您的应用程序免受暴力攻击,您必须实现某种速率限制。幸运的是,`NPM`上已经有很多各种中间件可用。其中之一是[express-rate-limit](https://github.com/nfriedly/express-rate-limit)。
```bash
$ npm i --save express-rate-limit
```
安装完成后,将其应用为全局中间件。
```typescript
import * as rateLimit from 'express-rate-limit';
// somewhere in your initialization file
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
}),
);
```
如果在服务器和以太网之间存在负载均衡或者反向代理,Express可能需要配置为信任proxy设置的头文件,从而保证最终用户得到正确的IP地址。要如此,首先使用`NestExpressApplication`平台[接口](https://docs.nestjs.com/first-steps#platform)来创建你的`app`实例,然后配置[trust proxy](https://expressjs.com/en/guide/behind-proxies.html)设置。
```TypeScript
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// see https://expressjs.com/en/guide/behind-proxies.html
app.set('trust proxy', 1);
```
> 如果使用 `FastifyAdapter`,用 [fastify-rate-limit](https://github.com/fastify/fastify-rate-limit)替换。
## 限速(新版)
保护应用程序免受暴力攻击的常用技术是速率限制。 要开始使用,您需要安装 `@nestjs/throttler` 包。
~~~bash
$ npm i --save @nestjs/throttler
~~~
安装完成后,可以使用 `forRoot` 或 `forRootAsync` 方法将 `ThrottlerModule` 配置为任何其他 Nest 包。
~~~typescript
@Module({
imports: [
ThrottlerModule.forRoot({
ttl: 60,
limit: 10,
}),
],
})
export class AppModule {}
~~~
上面将为受保护的应用程序的路由设置 `ttl` 的全局选项、生存时间和限制、`ttl` 内的最大请求数。
导入模块后,您可以选择绑定 `ThrottlerGuard `的方式。 守卫部分中提到的任何类型的绑定都可以。 例如,如果您想全局绑定守卫,可以通过将此提供程序添加到任何模块来实现:
~~~typescript
{
provide: APP_GUARD,
useClass: ThrottlerGuard
}
~~~
### 自定义
有时您可能希望将防护绑定到控制器或全局绑定,但希望禁用一个或多个端点的速率限制。 为此,您可以使用 `@SkipThrottle() `装饰器来否定整个类或单个路由的节流器。 `@SkipThrottle() `装饰器也可以接受一个布尔值,如果你想排除大部分控制器,但不是每条路由。
还有一个 `@Throttle() `装饰器可以用来覆盖全局模块中设置的限制和 ttl,以提供更严格或更宽松的安全选项。 这个装饰器也可以用于类或函数。 这个装饰器的顺序很重要,因为参数的顺序是限制,ttl。
### 代理
如果您的应用程序在代理服务器后面运行,请检查信任代理选项的特定 HTTP 适配器选项(`express` 和 `fastify`)并启用它。 这样做将允许您从 `X-Forwarded-For` 标头中获取原始 IP 地址,并且您可以覆盖 `getTracker()` 方法以从标头中而不是从 req.ip 中提取值。 以下示例适用于 `express` 和 `fastify`:
~~~ts
// throttler-behind-proxy.guard.ts
import { ThrottlerGuard } from '@nestjs/throttler';
import { Injectable } from '@nestjs/common';
@Injectable()
export class ThrottlerBehindProxyGuard extends ThrottlerGuard {
protected getTracker(req: Record<string, any>): string {
return req.ips.length ? req.ips[0] : req.ip; // individualize IP extraction to meet your own needs
}
}
// app.controller.ts
import { ThrottlerBehindProxyGuard } from './throttler-behind-proxy.guard';
@UseGuards(ThrottlerBehindProxyGuard)
~~~
>您可以在此处找到 [express](https://expressjs.com/en/api.html#req.ips) 和[fastify](https://www.fastify.io/docs/latest/Reference/Request/) 的 req Request 对象的 API。
### Websockets[#](#websockets)
该模块可以与 websocket 一起使用,但它需要一些类扩展。 您可以扩展 ThrottlerGuard 并覆盖 handleRequest 方法,如下所示:
~~~typescript
@Injectable()
export class WsThrottlerGuard extends ThrottlerGuard {
async handleRequest(
context: ExecutionContext,
limit: number,
ttl: number,
): Promise<boolean> {
const client = context.switchToWs().getClient();
const ip = client.conn.remoteAddress;
const key = this.generateKey(context, ip);
const ttls = await this.storageService.getRecord(key);
if (ttls.length >= limit) {
throw new ThrottlerException();
}
await this.storageService.addRecord(key, ttl);
return true;
}
}
~~~
>如果您使用的是 `@nestjs/platform-ws` 包,则可以改用 `client._socket.remoteAddress`。
### GraphQL[#](#graphql)
`ThrottlerGuard` 也可用于处理 GraphQL 请求。 同样,可以扩展保护,但这次 `getRequestResponse` 方法将被覆盖。
~~~typescript
@Injectable()
export class GqlThrottlerGuard extends ThrottlerGuard {
getRequestResponse(context: ExecutionContext) {
const gqlCtx = GqlExecutionContext.create(context);
const ctx = gqlCtx.getContext();
return { req: ctx.req, res: ctx.res }
}
}
~~~
### 配置
以下是`ThrottlerModule` 配置选项:
| | |
|--- |--- |
| `ttl` | 每个请求将在存储中持续的秒数 |
|`limit` | TTL 限制内的最大请求数 |
|`ignoreUserAgents` | 在限制请求时要忽略的用户代理正则表达式数组 |
|`storage` | 如何跟踪请求的存储设置 |
### 异步配置
您可能希望以异步方式而不是同步方式获取限速配置。 您可以使用 `forRootAsync()` 方法,该方法允许依赖注入和异步方法。
一种方法是使用工厂函数:
~~~typescript
@Module({
imports: [
ThrottlerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
ttl: config.get('THROTTLE_TTL'),
limit: config.get('THROTTLE_LIMIT'),
}),
}),
],
})
export class AppModule {}
~~~
您还可以使用 useClass 语法:
~~~typescript
@Module({
imports: [
ThrottlerModule.forRootAsync({
imports: [ConfigModule],
useClass: ThrottlerConfigService,
}),
],
})
export class AppModule {}
~~~
这是可行的,只要 `ThrottlerConfigService` 实现了接口 `ThrottlerOptionsFactory`。
### 存储
内置存储是一个内存缓存,它跟踪发出的请求,直到它们通过全局选项设置的 TTL。 只要类实现 `ThrottlerStorage` 接口,您就可以将自己的存储选项放入 `ThrottlerModule` 的存储选项中。
对于分布式服务器,您可以使用 Redis 的社区存储提供程序来获得单一的事实来源。
>`ThrottlerStorage` 可以从 `@nestjs/throttler` 导入。
- 介绍
- 概述
- 第一步
- 控制器
- 提供者
- 模块
- 中间件
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 自定义装饰器
- 基础知识
- 自定义提供者
- 异步提供者
- 动态模块
- 注入作用域
- 循环依赖
- 模块参考
- 懒加载模块
- 应用上下文
- 生命周期事件
- 跨平台
- 测试
- 技术
- 数据库
- Mongo
- 配置
- 验证
- 缓存
- 序列化
- 版本控制
- 定时任务
- 队列
- 日志
- Cookies
- 事件
- 压缩
- 文件上传
- 流式处理文件
- HTTP模块
- Session(会话)
- MVC
- 性能(Fastify)
- 服务器端事件发送
- 安全
- 认证(Authentication)
- 授权(Authorization)
- 加密和散列
- Helmet
- CORS(跨域请求)
- CSRF保护
- 限速
- GraphQL
- 快速开始
- 解析器(resolvers)
- 变更(Mutations)
- 订阅(Subscriptions)
- 标量(Scalars)
- 指令(directives)
- 接口(Interfaces)
- 联合类型
- 枚举(Enums)
- 字段中间件
- 映射类型
- 插件
- 复杂性
- 扩展
- CLI插件
- 生成SDL
- 其他功能
- 联合服务
- 迁移指南
- Websocket
- 网关
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 适配器
- 微服务
- 概述
- Redis
- MQTT
- NATS
- RabbitMQ
- Kafka
- gRPC
- 自定义传输器
- 异常过滤器
- 管道
- 守卫
- 拦截器
- 独立应用
- Cli
- 概述
- 工作空间
- 库
- 用法
- 脚本
- Openapi
- 介绍
- 类型和参数
- 操作
- 安全
- 映射类型
- 装饰器
- CLI插件
- 其他特性
- 迁移指南
- 秘籍
- CRUD 生成器
- 热重载
- MikroORM
- TypeORM
- Mongoose
- 序列化
- 路由模块
- Swagger
- 健康检查
- CQRS
- 文档
- Prisma
- 静态服务
- Nest Commander
- 问答
- Serverless
- HTTP 适配器
- 全局路由前缀
- 混合应用
- HTTPS 和多服务器
- 请求生命周期
- 常见错误
- 实例
- 迁移指南
- 发现
- 谁在使用Nest?