ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
## 限速 为了保护您的应用程序免受暴力攻击,您必须实现某种速率限制。幸运的是,`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` 导入。