守卫一般用于授权处理,判断当前请求是否能放行给路由处理程序。
执行顺序:中间件 - 守卫 - 拦截器 / 管道
使用守卫
- 创建 CRUD 模板:
nest g res cus-guard
- 创建守卫:
nest g gu cus-guard
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable() // 守卫也是使用 @Injectable() 修饰; 需要继承自 CanActivate
export class CusGuardGuard implements CanActivate {
// 需要实现 canActivate 方法; 该方法接收参数 ctx, 返回布尔值
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
console.log('经过了守卫');
return true; // 返回 truely 值表示放行, 否则当前请求会被拦截
}
}
- 通过
@UseGuards(XXX)
装饰器使用守卫
import { Controller, Get, UseGuards } from '@nestjs/common';
import { CusGuardGuard } from './cus-guard.guard';
import { CusGuardService } from './cus-guard.service';
@Controller('cus-guard')
@UseGuards(CusGuardGuard) // 使用守卫
export class CusGuardController {
constructor(private readonly cusGuardService: CusGuardService) {}
@Get()
findAll() {
return this.cusGuardService.findAll();
}
}
如果需要使用多个守卫,可以给 UseGuards 传入多个参数:@UseGuards(XXX, XXX, XXX)
。守卫会从左往右执行。若前面的守卫没有放行,则不会执行后面的守卫。
也可以把守卫放到指定方法前,表示只有这个方法需要鉴权
@Controller('cus-guard')
export class CusGuardController {
constructor(private readonly cusGuardService: CusGuardService) {}
@Get()
@UseGuards(CusGuardGuard) // 使用守卫
findAll() {
return this.cusGuardService.findAll();
}
}
全局守卫
- 创建守卫:
nest g gu cus-guard
- 使用
useGlobalGuards
注册全局守卫
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { CusGuardGuard } from './cus-guard/cus-guard.guard'; // 引入守卫
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new CusGuardGuard()); // 注册全局守卫
await app.listen(3000);
}
bootstrap();
配置角色权限
- 使用装饰器
@SetMetadata(key, value)
配置角色
import { Controller, Get, UseGuards, SetMetadata } from '@nestjs/common';
import { CusGuardGuard } from './cus-guard.guard';
import { CusGuardService } from './cus-guard.service';
@Controller('cus-guard')
@UseGuards(CusGuardGuard)
export class CusGuardController {
constructor(private readonly cusGuardService: CusGuardService) {}
@Get()
@SetMetadata('role', ['admin']) // 注入数据
findAll() {
return this.cusGuardService.findAll();
}
}
- 使用反射
Reflector
读取SetMetadata
的值
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
@Injectable()
export class CusGuardGuard implements CanActivate {
constructor(private reflector: Reflector) {} // 注入 Reflector 实例
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// 通过 Reflector 实例的 get 方法获取被注入的数据
const role = this.reflector.get<string[]>('role', context.getHandler());
console.log('role', role);
return true;
}
}
现在请求 /cus-guard
会在服务端打印 role [ 'admin' ]
假设我们通过 query 参数携带权限信息,我们需要根据该权限信息进行授权:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Request } from 'express';
import { Observable } from 'rxjs';
@Injectable()
export class CusGuardGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const role = this.reflector.get<string[]>('role', context.getHandler());
const req = context.switchToHttp().getRequest<Request>(); // 获取请求
const cusRole = req.query.role as string; // 获取 query 参数 role
const permission = role.includes(cusRole); // 判断权限
console.log('permission', permission);
return permission;
}
}
不携带 query 参数 role
/ role
值不为 'admin'
,都会抛出 403 ( Forbidden referer origin )