Nestjs AOP 编程之 Guard 的使用
概念
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
验证权限,就可以这样使用:
由于LoginGuard
返回的是 false
,此时访问 UserController
下的路由,因为没有权限都会返回 403
:
UserController
之外的路由还是正常的:
单个路由使用
也可以局部应用在某一个路由上,如下:
此时访问这个路由时,会返回 403
,如下:
访问其他路由就不会有问题:
全局使用
全局使用有两种方法,第一个就是在 main.ts
中:
注意,这种方式需要实例化 LoginGuard
。此时访问任意路由,都会因为无权限而返回 403
,这里就不多截图了。
第二种全局使用方法就是在 AppModule
中这样声明:
此时 LoginGuard
也会自动作用到全局。你可能会疑惑这种全局声明跟在 main.ts
中的使用方式,除了写法的区别,还有什么别的不同?直接告诉你,就是这种在 providers
中声明的 Guard,是在 Ioc 容器里的,是可以注入别的 provider 的。这里不详细介绍 Ioc 容器和 provider 的概念,简单举个例子:
假如我们有一个工具模块,它的 Service 中提供了一些工具方法,比如:
现在我们希望在 LoginGuard
中使用这个 getCurrentTime
方法,就可以直接在 login.guard.ts
中这样注入使用:
使用结果:
这个就是 Nestjs 的依赖注入机制,不明白的需要补一下基础。而这种注入使用其他 Service 的方式,使用 app.useGlobalGuards(new LoginGuard())
的方式是不支持的,会报错:
但可以手动实例化来达到效果,这样:
手动实例化后,使用 getCurrentTime
方法就不会有限制了。但依赖注入是 Nestjs 的灵魂,能不手动实例化的就不要自己手动到处实例化,当项目规模大了之后,就会体会依赖注入的优势。所以还是推荐第二种在 providers 中声明全局 Guard 的方式。
总结
本文主要介绍了 Nestjs 中的 Guard,它是一种机制,可以保护路由不被不授权的情况下访问。如果 Guard 阻止了请求,则该请求不会达到其目标处理程序,并且 Guard 可以决定返回什么样的响应给客户端。
Guard 有三种使用方式:
- 用在 controller 上,作用此controller下所有的路由
- 用在单个路由上,只对当前路由生效
- 全局使用,两种方式:第一种是在
main.ts
中使用app.useGlobalGuards()
注册实例使用,第二种是在AppModule
中 providers 上全局声明。推荐第二种,因为可以依赖注入。
转载自:https://juejin.cn/post/7395392375084744741