跳转至

速度限制

保护应用程序免受蛮力攻击的一种常见技术是限速。 首先,你需要安装@nestjs/throttler包。

$ npm i --save @nestjs/throttler

一旦安装完成,ThrottlerModule可以配置为任何其他带有forRootforRootAsync方法的 Nest 包。

1
2
3
4
5
6
7
8
9
@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60,
      limit: 10,
    }),
  ],
})
export class AppModule {}

上面将为你的应用程序被保护的路由设置全局选项ttl,生存时间,和limit, ttl 内的最大请求数量。

一旦模块被导入,你就可以选择如何绑定ThrottlerGuardguards部分中提到的任何类型的绑定都可以。 例如,如果你想全局绑定这个守卫,你可以通过将这个 provider 添加到任何模块来实现:

1
2
3
4
{
  provide: APP_GUARD,
  useClass: ThrottlerGuard
}

定制

There may be a time where you want to bind the guard to a controller or globally, but want to disable rate limiting for one or more of your endpoints. For that, you can use the @SkipThrottle() decorator, to negate the throttler for an entire class or a single route. The @SkipThrottle() decorator can also take in a boolean for if there is a case where you want to exclude most of a controller, but not every route.

There is also the @Throttle() decorator which can be used to override the limit and ttl set in the global module, to give tighter or looser security options. This decorator can be used on a class or a function as well. The order for this decorator does matter, as the arguments are in the order of limit, ttl.

代理

如果您的应用程序运行在代理服务器后面,请检查特定的 HTTP 适配器选项(expressfastify)以获取“信任代理”选项并启用它。 这样做将允许你从X-Forward-For头获取原始 IP 地址,你可以重写getTracker()方法来从头而不是从req.ip获取值。 下面的例子同时适用于 express 和 fastify:

// 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)

Hint

You can find the API of the req Request object for express here and for fastify here.

Websockets

这个模块可以使用 websockets,但它需要一些类扩展。 你可以像这样扩展ThrottlerGuard并覆盖handleRequest方法:

@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;
  }
}

Hint

如果你正在使用@nestjs/platform-ws 包,你可以使用client._socket.remoteAddress代替。

GraphQL

The ThrottlerGuard can also be used to work with GraphQL requests. Again, the guard can be extended, but this time the getRequestResponse method will be overridden

1
2
3
4
5
6
7
8
@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 };
  }
}

配置

The following options are valid for the ThrottlerModule:

ttl 每个请求在存储中持续的秒数
limit TTL限制内的最大请求数
ignoreUserAgents 在限制请求时可以忽略的用户代理的正则表达式数组
storage 用于跟踪请求的存储设置

异步配置

You may want to get your rate-limiting configuration asynchronously instead of synchronously. You can use the forRootAsync() method, which allows for dependency injection and async methods.

One approach would be to use a factory function:

@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语法:

1
2
3
4
5
6
7
8
9
@Module({
  imports: [
    ThrottlerModule.forRootAsync({
      imports: [ConfigModule],
      useClass: ThrottlerConfigService,
    }),
  ],
})
export class AppModule {}

这是可行的,只要ThrottlerConfigService实现接口ThrottlerOptionsFactory

存储

内建存储是一个内存缓存,它跟踪请求,直到它们传递了由全局选项设置的 TTL。 你可以把你自己的存储选项放到ThrottlerModulestorage选项中,只要这个类实现了ThrottlerStorage接口。

对于分布式服务器,你可以使用社区存储提供器Redis来获得单一的真相来源。

!!! info Note ThrottlerStorage可以从@nestjs/throttler中导入。