NestJS 系列教程 - 守卫

引言

守卫(Guards)!正如其名,它的作用就是阻止未经授权的访问。守卫是大多数后端框架中的常见概念,无论是框架原生提供还是开发者自定义实现。NestJS 让我们能轻松保护 API 免受未授权或未认证用户的访问。

与管道(pipes)和过滤器(filters)类似,NestJS 中的守卫使用 @Injectable() 装饰器。每个守卫都必须实现 CanActivate 接口。该接口的属性让开发者能轻松编写自定义守卫逻辑。

中间件 vs 守卫

中间件(middleware)完全不知道后续要执行什么操作。而守卫通过 ExecutionContext 实例可以精确知晓后续要执行的操作。它们更像是过滤器和管道,能在请求-响应周期的适当时机插入逻辑。这一特性表明中间件是"被动"的。

守卫的执行顺序是在所有中间件之后,但在管道和拦截器之前。以下示例代码(来自 NestJS 官方文档)展示了基本结构:

typescript 复制代码
@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    // 验证请求对象中的角色和权限的代码
  }
}

执行上下文

ExecutionContext 类继承自 ArgumentsHost,提供了切换不同上下文的方法(如 HTTP、WebSocket、GraphQL)。通过 switchToHttp() 等方法可以获取特定上下文对象。

守卫绑定

守卫可以应用在三个作用域: • 全局作用域控制器作用域方法作用域

以下示例展示控制器级别的守卫绑定:

typescript 复制代码
@Controller('pokemons')
@UseGuards(AuthGuard)
export class PokemonController {}

装饰器支持传递守卫类、实例或列表。

角色控制

角色系统用于限制端点访问权限。NestJS 通过自定义元数据实现角色验证:

typescript 复制代码
@Post("/updateAccess")
@SetMetadata('roles', ['admin', 'superadmin'])
async updateReadWriteAccessofUser(@Body() inputDto: any): Promise<boolean> {
  this.adminService(inputDto);
}

更优雅的做法是创建专用装饰器(遵循 DRY 原则):

typescript 复制代码
import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

使用方式:

typescript 复制代码
@Post("/updateAccess")
@Roles(["admin", "superadmin"])
async updateReadWriteAccessofUser(@Body() inputDto: any): Promise<boolean> {
  this.adminService(inputDto);
}

完整角色守卫实现

typescript 复制代码
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) return true;

    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return verifyRoles(roles, user.roles);
  }
}

// 组合装饰器
export function Roles(...roles: string[]) {
  return applyDecorators(
    SetMetadata('roles', roles),
    UseGuards(RolesGuard),
  );
}

Reflector 工具类用于获取控制器方法的元数据。验证失败时会触发异常处理层(全局异常过滤器或当前上下文的异常过滤器)。

相关推荐
悟空瞎说2 天前
NestJS 接口设计避坑:摒弃万能用户更新接口,落地单一职责与最小权限原则
后端·nestjs
光影少年5 天前
HashRouter 和 BrowserRouter 区别、底层原理、部署差异
前端·react.js·nestjs
悟空瞎说5 天前
NestJS 12 预览版重磅来袭:全面原生 ESM 正式落地
nestjs
刚子编程17 天前
从零开始:在 Windows 服务器上部署 Node.js 项目(小白实战教程)
服务器·nestjs·pm2·windowsserver·node.js部署·caddy反向代理
CSharp精选营17 天前
从零开始:在 Windows 服务器上部署 Node.js 项目(小白实战教程)
nestjs·pm2·windowsserver·node.js部署·caddy反向代理
晓杰'18 天前
从0到1实现Balatro游戏后端(8):Skip Blind与Tag奖励机制设计与实现
后端·websocket·typescript·项目实战·nestjs·状态管理·游戏服务器
小蜜蜂dry22 天前
nestjs实战-权限二:角色模块
前端·后端·nestjs
小蜜蜂dry22 天前
nestjs实战-权限一: 菜单模块
前端·后端·nestjs
妖孽白YoonA25 天前
xlt-token v1.0.0 正式发布:NestJS / Express 一包接入的 Token 鉴权库
后端·node.js·nestjs
晓杰'1 个月前
从0到1实现Balatro游戏后端(7):Boss Blind与特殊规则实现
后端·websocket·typescript·node.js·游戏开发·项目实战·nestjs