参考:
Nest.js:https://docs.nestjs.cn/
守卫:https://docs.nestjs.cn/9/guards
基本使用
在 Nest.js 中,守卫是一个使用 @Injectable()
装饰器的类,守卫应该实现 CanActivate
接口。
一个简单的例子如下:
// auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request); // 在此处编写自己的校验逻辑,返回true表示可以访问,false为禁止访问
}
}
深入使用
自定义元数据
在守卫的执行过程中,难免会需要根据不同的情况做不同的处理,此时就需要用到自定义元数据。
在 Nest.js 中,自定义元数据是非常方便的,可以使用 @SetMetadata()
装饰器将定制元数据。
// cats.controller.ts
@Post()
@SetMetadata('roles', ['admin']) // 在这里就将 元数据 附加了上去,key 为 roles ,value 为 ['admin'],至于怎么获取元数据,后面会说。
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
很显然的是,直接调用 @SetMetadata()
并不是一个很好的做法,更好的写法是创建自己的装饰器,例如:
// roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
之后只需要在要用到的地方调用即可。
// cats.controller.ts
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
使用元数据
在自定义了元数据之后,我们还需要拿到元数据的值才能使用,在这里可以使用 @nestjs/core
中提供的 Reflector
帮助类。
// roles.guard.ts
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()); // 这一步就能拿到元数据了,需要注意的是,context.getHandler() 只能获取到 方法 上的元数据,无法获取 类 上的元数据。
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return matchRoles(roles, user.roles);
}
}
元数据扩展
更多相关内容可参考:应用上下文
在上一节中,我们通过 context.getHandler()
来获取元数据,但这里获取的元数据只能是 方法
的,无法获取到 类
的,那么要怎么获取到 类
的元数据呢?
答案是通过 context.getClass()
,即:
const roles = this.reflector.get<string[]>('roles', context.getClass());
那么接下来问题来了,如果要先从 类
获取元数据,再从 方法
获取元数据,要怎么办呢?
Nest.js 也提供了相应的方案。
// cats.controller.ts
@Roles('user')
@Controller('cats')
export class CatsController {
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
}
// 如果是前者覆盖后者,则使用 getAllAndOverride(),此时将生成包含 ['admin']的 roles
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
// 如果是两者合并,则使用 getAllAndMerge(),此时将生成包含 ['user', 'admin']的roles
const roles = this.reflector.getAllAndOverride<string[]>('roles', [
context.getHandler(),
context.getClass(),
]);
总结
通过 @SetMetadata()
设置元数据,再通过 Reflector
获取元数据,我们就可以非常方便的在守卫、拦截器、管道或者其他 Nest.js 组件中使用元数据,极大的简化了装饰器的使用。
- 本文链接: https://wp.cmyr.ltd/archives/the-use-of-guards-in-nest-js
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
欢迎关注我的其它发布渠道
发表回复
要发表评论,您必须先登录。