Nestjs自定義註解實現接口權限控制詳解

正文

當業務接口開發完成之後,正式上線之前還需要對每個接口進行權限控制。比如刪除用戶隻能管理員角色操作,查詢全部用戶隻有管理員有權限等。

對接口實現權限控制有很多種方案,最簡單的實現是在每個接口的 controller 層進行判斷,但這樣不優雅,冗餘代碼多,且對業務侵入比較強。合理的權限控制方案應該是跟業務邏輯松耦合。

Nest 官方文檔為我們提供瞭幾種權限控制方式,我們來實現最基礎的基於角色的 RBAC(Role-based access control) 控制方案。

實現 RBAC 權限控制一共需要五步:

  • 定義角色的枚舉類 Role
  • 聲明自定義註解(裝飾器)roles.decorators.ts
  • 實現角色守衛 RolesGuard
  • 在 app.module.ts 的 providers 中引入 RolesGuard
  • 在需要的接口上添加註解:@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);
  }

至此,使用自定義註冊實現接口權限控制就完成啦。

以上就是Nestjs自定義註解實現接口權限控制詳解的詳細內容,更多關於Nestjs註解接口權限的資料請關註WalkonNet其它相關文章!

推薦閱讀: