likes
comments
collection
share

Nestjs AOP 编程之 Guard 的使用

作者站长头像
站长
· 阅读数 22

概念

Guard 是路由守卫的意思,在 NestJS 中,它是一种用于保护路由处理器免受未经授权访问的机制。Guard 在请求到达处理程序之前运行,可以根据特定条件(如用户认证状态或角色权限)决定是否允许请求继续。通过实现 CanActivate 接口并返回一个布尔值,Guard 提供了一种灵活的方式来控制对特定路由或控制器方法的访问权限。

简单说,就是通过 Guard 返回一个布尔值,为 true 表示请求可以继续,为 false 就表示无权限继续访问。主要用于可以提高程序的安全性和健壮性。

创建

先在 Nestjs 项目中创建一个 Guard:

nest g guard login --no-spec --flat

会在 src 目录下生成一个 login.guard.ts 文件,内容如下:

// login.guard.ts

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

@Injectable()
export class LoginGuard implements CanActivate {
  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    return true;
  }
}

代码中得知,我们自定义的 Guard 类 LoginGuard 需要实现 CanActivate 接口,此接口只有一个方法 canActivate,这个方法返回三种布尔类型,默认就是返回 true,表示不拦截接口。

我们现在改一下这个方法的逻辑,加一句打印,然后返回 false

// login.guard.ts

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

@Injectable()
export class LoginGuard implements CanActivate {
  canActivate(
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {
    
    console.log('login check');
    return false;
    
  }
}

然后就是使用这个 Guard。

使用

整个 Controller 使用

可以使用在某个 Controller 上,这样其下面的所有路由都会被应用上这个 Guard,比如我们希望访问用户模块时,都要使用 LoginGuard 验证权限,就可以这样使用:

Nestjs AOP 编程之 Guard 的使用

由于LoginGuard 返回的是 false,此时访问 UserController 下的路由,因为没有权限都会返回 403

Nestjs AOP 编程之 Guard 的使用

Nestjs AOP 编程之 Guard 的使用

UserController 之外的路由还是正常的:

Nestjs AOP 编程之 Guard 的使用

单个路由使用

也可以局部应用在某一个路由上,如下:

Nestjs AOP 编程之 Guard 的使用

此时访问这个路由时,会返回 403,如下:

Nestjs AOP 编程之 Guard 的使用

访问其他路由就不会有问题:

Nestjs AOP 编程之 Guard 的使用

全局使用

全局使用有两种方法,第一个就是在 main.ts 中:

Nestjs AOP 编程之 Guard 的使用

注意,这种方式需要实例化 LoginGuard。此时访问任意路由,都会因为无权限而返回 403,这里就不多截图了。

第二种全局使用方法就是在 AppModule 中这样声明:

Nestjs AOP 编程之 Guard 的使用

此时 LoginGuard 也会自动作用到全局。你可能会疑惑这种全局声明跟在 main.ts 中的使用方式,除了写法的区别,还有什么别的不同?直接告诉你,就是这种在 providers 中声明的 Guard,是在 Ioc 容器里的,是可以注入别的 provider 的。这里不详细介绍 Ioc 容器和 provider 的概念,简单举个例子:

假如我们有一个工具模块,它的 Service 中提供了一些工具方法,比如:

Nestjs AOP 编程之 Guard 的使用

现在我们希望在 LoginGuard 中使用这个 getCurrentTime 方法,就可以直接在 login.guard.ts 中这样注入使用:

Nestjs AOP 编程之 Guard 的使用

使用结果:

Nestjs AOP 编程之 Guard 的使用

这个就是 Nestjs 的依赖注入机制,不明白的需要补一下基础。而这种注入使用其他 Service 的方式,使用 app.useGlobalGuards(new LoginGuard()) 的方式是不支持的,会报错:

Nestjs AOP 编程之 Guard 的使用

但可以手动实例化来达到效果,这样:

Nestjs AOP 编程之 Guard 的使用

手动实例化后,使用 getCurrentTime 方法就不会有限制了。但依赖注入是 Nestjs 的灵魂,能不手动实例化的就不要自己手动到处实例化,当项目规模大了之后,就会体会依赖注入的优势。所以还是推荐第二种在 providers 中声明全局 Guard 的方式。

总结

本文主要介绍了 Nestjs 中的 Guard,它是一种机制,可以保护路由不被不授权的情况下访问。如果 Guard 阻止了请求,则该请求不会达到其目标处理程序,并且 Guard 可以决定返回什么样的响应给客户端。

Guard 有三种使用方式:

  • 用在 controller 上,作用此controller下所有的路由
  • 用在单个路由上,只对当前路由生效
  • 全局使用,两种方式:第一种是在 main.ts 中使用 app.useGlobalGuards() 注册实例使用,第二种是在 AppModule 中 providers 上全局声明。推荐第二种,因为可以依赖注入。
转载自:https://juejin.cn/post/7395392375084744741
评论
请登录