Nest框架中的守卫
根据运行时出现的某些条件(例如权限,角色,访问控制列表等)来确定给定的请求,是否由路由处理程序处理
在Express
中,用的是中间件处理授权和认证。但是,中间件调用next()
函数后,不明确那个执行处理程序,守卫比较明确。
执行时机:守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。
创建守卫
在终端执行 $ nest g guard userGuard
$ nest g guard userGuard
授权守卫
只有当调用者(通常是经过身份验证的特定用户)具有足够的权限时,特定的路由才可用。假设用户是经过身份验证的(因此,请求头附加了一个token
)。守卫将提取和验证token
,并使用提取的信息来确定请求是否可以继续。
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
// 定义了一个名为UserGuardGuard的守卫,该守卫实现了CanActivate接口
export class UserGuardGuard implements CanActivate {
// 实现了CanActivate接口中的canActivate方法,该方法接收一个执行上下文(ExecutionContext)参数,返回一个布尔值、Promise对象或Observable对象
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
// 执行上下文中的switchToHttp方法获取到HttpServer对象,并调用其getRequest方法获取到当前的请求对象
const request = context.switchToHttp().getRequest();
// 对请求进行校验,并返回校验结果
return validateRequest(request);
}
}
注入器(Injectable)、守卫接口(CanActivate)和执行上下文(ExecutionContext)
UserGuardGuard
实现了CanActivate
接口,用于在请求到达控制器之前进行身份验证和授权检查。在canActivate
方法中,使用ExecutionContext
获取当前请求的对象,并将其传递给validateRequest
函数进行验证。
validateRequest
函数返回一个boolean值
,表示该请求是否具有访问权限。如果返回true,则请求将被允许进入控制器;如果返回false,则请求将被拒绝,并返回相应的错误响应
绑定守卫
第一种情况:类型绑定
传递了 RolesGuard
类型而不是实例, 框架内部会进行实例化,并启用了依赖注入
@Controller('user')
@UseGuards(RolesGuard)
export class CatsController {}
第二种情况:实例绑定
@Controller('user')
@UseGuards(new RolesGuard())
export class CatsController {}
第三种情况:全局绑定(全局守卫)
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
全局绑定问题:全局守卫,不能插入依赖项
解决方案:
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
控制用户角色权限
@SetMetadata()
装饰器将定制元数据附加到路由处理程序的能力。这些元数据提供了我们所缺少的角色数据,而守卫需要这些数据来做出决策。
@Post()
// 该方法需要 `admin` 角色的用户才能访问
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
在方法上使用了@SetMetadata装饰器,将一个key为'roles',value为['admin']的元数据附加到该方法上。
在实际开发中,一般用自定义的装饰器,更加灵活方便
例如:
// 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);
}
综合运用
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());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return matchRoles(roles, user.roles);
}
}
详解:
AuthGuard
实现了CanActivate
接口,用于在请求到达控制器之前进行身份验证和授权检查。在RolesGuard
中,使用Reflector
注入依赖,以便在canActivate
方法中获取控制器方法上的元数据。在
canActivate
方法中,使用this.reflector.get()
方法获取context.getHandler()
对应的控制器方法上的roles
元数据。如果该元数据不存在,则直接返回true,表示该请求具有访问权限。否则,获取当前请求的用户信息,并使用matchRoles
函数进行角色匹配,如果匹配成功,则返回true,否则返回false。
转载自:https://juejin.cn/post/7244808577209188413