Nestjs 自定义注解实现接口权限控制

本文介绍了如何在 Nestjs 中实现基于角色的 RBAC 权限控制,包括定义角色枚举、创建自定义注解、实现角色守卫、引入守卫到模块以及在接口上使用注解进行权限校验。通过这些步骤,可以优雅地实现接口的权限管理,降低业务逻辑的侵入性。

当业务接口开发完成之后,正式上线之前还需要对每个接口进行权限控制。比如删除用户只能管理员角色操作,查询全部用户只有管理员有权限等。对接口实现权限控制有很多种方案,最简单的实现是在每个接口的 controller 层进行判断,但这样不优雅,冗余代码多,且对业务侵入比较强。合理的权限控制方案应该是跟业务逻辑松耦合。Nest 官方文档为我们提供了几种权限控制方式,我们来实现最基础的基于角色的 RBAC(Role-based access control) 控制方案。实现 RBAC 权限控制一共需要五步:

1.定义角色的枚举类 Role
2.声明自定义注解(装饰器)roles.decorators.ts
3.实现角色守卫 RolesGuard
4.在 app.module.ts 的 providers 中引入 RolesGuard
5.在需要的接口上添加注解:@Roles(Role.Admin, Role.SuperAdmin)�

定义角色枚举

梳理好系统中一共有多少种角色,并为每种角色确定好 Code,然后声明为枚举类型。这里的角色类型可以自定义,根据业务需要设多少都行。

export enum Role {SuperAdmin = 'SuperAdmin',Admin = 'Admin',Normal = 'Normal',Guest = 'Guest',
} 

声明自定义注册(装饰器)

新建一个 roles.decorator.ts文件,就可以在 Controller 中使用 @Roles注解了

import { SetMetadata } from '@nestjs/common';
import { Role } from '@constants';

export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles); 

实现角色守卫 RolesGuard

新建 roles.gurad.ts文件,它的作用是告诉 nest 什么情况下请求应该被拦截,什么情况下请求应该被通过。通过 reflector可以拿到注解中传入的角色 code 列表,通过 context.switchToHttp().getRequest()可以拿到 request 对象。因为我们使用了 jwt,在 JwtStrategy�中往 user 对象中写入了当前登录用户的角色信息,所以可以通过从 request 中取 user 的方式得到角色列表。这块具体的获取方式根据你的业务而定,业务中把角色信息放在了什么地方,就从哪里取。拿到注解中传入的角色,和用户已有的角色进行对比,如果用户角色中包含需要的角色,则返回 true,如果返回 false,则会抛出默认的异常如下:

{"statusCode": 403,"message": "Forbidden resource","error": "Forbidden"
} 

我们需要定制错误信息,以使整个系统保持一致,则抛出一个自定义异常,这个异常按照统一规定的格式返回错误信息。

import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from '../decorators/roles.decorator';
import { Role } from '@constants';
import { InternalErrorException } from '../../exception/InternalErrorException';
import { errorResult } from '@utils';
import { ErrorCodes } from '@constants/errorCode';

@Injectable()
export class RolesGuard implements CanActivate {constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext): boolean {const requiredRoles = this.reflector.getAllAndOverride<Role[]>(ROLES_KEY, [context.getHandler(),context.getClass(),]);if (!requiredRoles) {return true;}const { user } = context.switchToHttp().getRequest();const roleCodes = user.roles?.map((item) => item.code);const flag = requiredRoles.some((role) => roleCodes?.includes(role));if (!flag) {throw new InternalErrorException(errorResult(ErrorCodes.NoAuth));}return flag;}
} 

在 providers 中引入 RolesGuard

如果想在全局使用自定义注解,就在 app.module.ts中全局引入。如果仅想在某个 controller 中使用,则在这个 controller 层级的 module 中声明。

providers: [{provide: APP_GUARD,useClass: RolesGuard,},
], 

使用注解

在接口上使用自定义注解实现权限校验。括号里可以写一个权限,也可以写多个,因为我们 roles.decorator.ts中写的参数类型为 Role[]

 @Get()@Roles(Role.Admin, Role.SuperAdmin)async findAll() {const users = await this.userService.findAll();return successResult(users);} 

至此,使用自定义注册实现接口权限控制就完成啦。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值