Nest:守卫 guard:请求授权的利器

Nest.js 中的守卫(Guards))可以用于处理请求授权。

守卫是一个使用 @Injectable() 装饰器注解的类,它实现了 CanActivate 接口。守卫可以决定是否允许某个请求继续执行,通常是通过验证用户是否拥有执行操作的权限。

如果请求被允许执行,守卫应该返回 true 或者一个 ObservablePromise,这个 ObservablePromise 解析为 true

如果请求不被允许,守卫应该返回 false 或者一个解析为 falseObservablePromise,或者抛出一个异常。

守卫示例:

typescript 复制代码
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Request } from 'express';

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest<Request>();
    const cookies = request.cookies; // 获取 Cookie
    const session = request.session; // 获取 Session

    const user = session.user || request.user;
    // 校验用户是否拥有特定的角色
    return user && user.roles.includes('admin');
  }
}

这个方法接收一个 ExecutionContext 参数,它提供了请求的详细信息。

canActivate 方法中,我们可以获取到当前请求的用户信息,并检查用户是否拥有 'admin' 角色。

如果用户是 'admin',则返回 true,请求继续执行;如果不是,则返回 false,请求将被拒绝。

使用 @UseGuards() 装饰器应用守卫:

typescript 复制代码
import { Controller, Get, UseGuards } from '@nestjs/common';
import { RolesGuard } from './roles.guard';

@Controller('items')
export class ItemsController {
  @Get()
  @UseGuards(RolesGuard)
  findAll() {
    // 只有当 RolesGuard 允许时,才会执行这里的代码
    return 'This route is protected by a roles guard';
  }
}

在上面的代码中,ItemsControllerfindAll 方法被 @UseGuards(RolesGuard) 装饰。

这意味着每次调用 findAll 方法时,RolesGuard 都会被触发,以确保用户具有正确的角色。

配置守卫方式

  1. 控制器级别的守卫
typescript 复制代码
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {
  // 所有 CatsController 中的路由都将受到 RolesGuard 的保护
}
  1. 方法级别的守卫
typescript 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  @UseGuards(RolesGuard)
  findOne() {
    // 只有 findOne 方法受到 RolesGuard 的保护
  }
}
  1. 全局守卫
typescript 复制代码
@Module({
  // ...
  providers: [
    {
      provide: APP_GUARD,
      useClass: RolesGuard,
    },
  ],
})
export class AppModule {}

通过 provider 的方式声明的 Guard 是在 IoC 容器里的,可以注入别的 provider:

typescript 复制代码
@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService) {}

  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    // 在这里可以使用注入的 AuthService 进行身份验证逻辑
    return this.authService.isAuthenticated();
  }
}
  1. 模块级别的守卫 :虽然 Nest.js 直接不支持模块级别的守卫配置,但你可以在模块的 providers 数组中配置守卫,并使用模块的导出功能来共享这个守卫。这样,其他导入了该模块的模块可以复用同一个守卫。
typescript 复制代码
@Module({
  providers: [RolesGuard],
  exports: [RolesGuard],
})
export class SharedModule {}

动态守卫

动态守卫指的是可以根据特定的条件或参数动态创建的守卫。

这种守卫通常是通过工厂函数或者类的静态方法来实现的,允许开发者在运行时根据需要构造不同的守卫实例。

动态守卫的一个常见用例是基于角色的访问控制,其中你可能希望根据不同的角色来限制对路由的访问。

下面是一个如何实现动态守卫的示例:

  1. 创建一个接受参数的工厂函数:该函数接受一些参数(例如角色列表),并返回一个新的守卫实例
typescript 复制代码
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private readonly roles: string[]) {}

  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return this.roles.some((role) => user.roles.includes(role));
  }
}

export function createRolesGuard(...roles: string[]): Type<CanActivate> {
  @Injectable()
  class DynamicRolesGuard extends RolesGuard {
    constructor() {
      super(roles);
    }
  }
  return DynamicRolesGuard;
}

在上面的代码中,createRolesGuard 是一个工厂函数,它接受一个角色列表并返回一个继承自 RolesGuard 的新守卫类。

新类 DynamicRolesGuard 通过构造函数将角色列表传递给 RolesGuard

  1. 在控制器中使用动态守卫 :控制器中使用 @UseGuards() 装饰器和工厂函数来应用动态创建的守卫
typescript 复制代码
@Controller('cats')
export class CatsController {
  @Get()
  @UseGuards(createRolesGuard('admin', 'moderator'))
  findAll() {
    // 只有 'admin' 或 'moderator' 角色的用户可以访问
  }

  @Post()
  @UseGuards(createRolesGuard('admin'))
  create() {
    // 只有 'admin' 角色的用户可以访问
  }
}

在上面的例子中,findAll 方法使用了一个允许 'admin' 或 'moderator' 角色的用户访问的动态守卫,而 create 方法使用了一个只允许 'admin' 角色用户访问的守卫。

良好运用动态守卫能减少很多重复代码,提高灵活性。

相关推荐
耶啵奶膘29 分钟前
uniapp-是否删除
linux·前端·uni-app
NiNg_1_2341 小时前
SpringBoot整合SpringSecurity实现密码加密解密、登录认证退出功能
java·spring boot·后端
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
Chrikk2 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*2 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue2 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang
cs_dn_Jie3 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js