NestJS之14- 守卫

1,138 阅读3分钟

1. 什么是守卫

Nest.js 中的守卫(Guard)是一种特殊类型的中间件,可以用于保护路由,控制谁可以访问某个路由。守卫是用来验证请求是否有权限进行请求的,如果请求有权限,守卫会让请求通过到达路由处理程序;如果请求没有权限,守卫会拒绝请求,并返回特定的 HTTP 响应。

Nest.js 守卫能够进行基于角色、权限、访问令牌等多种验证方式,常常用于鉴权和授权操作。守卫可以在路由处理程序执行之前或之后拦截请求,并在验证通过或不通过后执行一些操作,例如记录日志、重定向或抛出异常。

在 Nest.js 中,守卫可以是全局的或局部的,如果是全局的,那么它将适用于应用程序中的所有路由,如果是局部的,那么它只适用于特定的路由。守卫也可以链式调用,允许多个守卫共同验证请求。

守卫的执行顺序

守卫的执行顺序取决于它们在路由处理管道中的位置。在 Nest.js 中,请求会经过一系列处理管道,包括守卫、中间件和控制器的顺序调用。

路由处理管道的顺序如下:

  1. 全局守卫:全局守卫按照它们在应用程序模块中提供程序的位置顺序执行,先执行模块中最后注册的全局守卫,再执行模块中前面注册的全局守卫。
  2. 中间件:中间件按照它们在模块中注册的顺序执行,先执行模块中注册的最后一个中间件,再执行模块中前面注册的中间件。
  3. 路由守卫:路由守卫按照它们在路由中定义的顺序执行,先执行路由定义中最后一个守卫,再执行前面定义的守卫。
  4. 控制器方法:控制器方法按照它们在控制器类中定义的顺序执行,先执行类定义的最后一个方法,再执行前面定义的方法。

守卫在控制器方法之前执行,而拦截器则在控制器方法执行前后执行,它们的处理顺序为:路由守卫 > 全局守卫 > 拦截器 > 中间件 > 控制器方法 > 响应拦截器。

守卫种类

  • AuthGuard:保护路由的基本守卫,用于验证用户是否已经通过身份验证。
  • RolesGuard:用于验证用户是否有足够的角色来执行某个操作。
  • JwtAuthGuard:基于 JSON Web Tokens(JWT)进行身份验证的守卫。
  • ThrottlerGuard:用于限制请求数量和请求速率,防止恶意攻击。
  • RateLimitGuard:基于令牌桶算法实现的流量限制守卫,可以控制请求的频率和速率。

2. 守卫的方式

2.1 局部守卫

先生成一个role的守卫 nest g gu role

role.guard.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';

@Injectable()
export class RoleGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('打印***context 守卫', context);
    return true;
  }
}

guard.controller.ts

import { RoleGuard } from './role.guard';

@Controller('guard')
@UseGuards(RoleGuard)
export class GuardController {}

2.2 全局守卫

main.ts

import { RoleGuard } from './guard/role.guard';

app.useGlobalGuards(new RoleGuard())

举例:

guard.controller.ts

import {
  Controller,
  Get,
  UseGuards,
  SetMetadata,
} from '@nestjs/common';
import { GuardService } from './guard.service';
import { RoleGuard } from './role.guard';

@Controller('guard')
@UseGuards(RoleGuard)
export class GuardController {
  constructor(private readonly guardService: GuardService) {}

  @Get()
  @SetMetadata('role', ['admin']) // 自定义名称 数据
  findAll() {
    return this.guardService.findAll();
  }
}

取数据时用定义的键 role

role.guard.ts

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import type { Request } from 'express';

@Injectable()
export class RoleGuard implements CanActivate {
  constructor(private reflector: Reflector) {}
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    console.log('打印***context 守卫', context);
    const admin = this.reflector.get<string[]>('role', context.getHandler()); // [ 'admin' ]
    // ?role=admin 进行定义 有就访问
    const req = context.switchToHttp().getRequest<Request>();
    console.log(req.query.role);
    if (admin.includes(req.query.role as string)) {
      return true;
    } else {
      return false;
    }
  }
}

访问 http://localhost:3000/guard?role=admfj

image.png