Nestjs项目实战 基于角色的访问控制(RBAC)实现

在Nestjs项目中实现基于角色的访问控制(RBAC)时,如何优雅地定义角色和权限的层级关系?目前我的权限校验是通过装饰器实现的,但感觉权限判断逻辑分散在各个控制器里不够集中。想请教:

  1. 角色和权限数据应该存储在数据库还是硬编码在代码中更合适?
  2. Nestjs的Guard和Interceptor在RBAC实现中应该如何分工配合?
  3. 有没有最佳实践来处理角色继承或组合权限的情况?
  4. 项目规模较大时,如何避免频繁查询数据库造成的性能问题?
3 回复

在NestJS中实现基于角色的访问控制(RBAC)可以通过中间件或守卫来完成。首先定义角色和权限模型,比如RoleEnum包含ADMIN, USER等。

  1. 创建一个装饰器@Roles(),接收允许的角色参数。
  2. 编写守卫roles.guard.ts,解析用户角色并与方法上的角色进行比较。
  3. 在控制器方法上使用@UseGuards(RolesGuard)并添加@Roles('ADMIN')装饰器。
  4. 数据库存储角色与权限的映射关系,通过服务获取当前用户的角色信息。

示例代码:

@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 userRole = request.user.role; // 假设从JWT中获取角色
    return roles.includes(userRole);
  }
}

在模块中注册守卫:

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

在NestJS项目中实现基于角色的访问控制(RBAC),可以按照以下步骤:

  1. 定义角色和权限:创建一个Role实体和一个Permission实体。角色可以拥有多个权限,权限是基本的操作单位。

  2. 用户模型关联角色:在User模型中添加一个字段来存储用户的角色。可以通过一对多的关系实现。

  3. 中间件拦截请求:创建一个全局或局部中间件,用于检查用户的权限。例如,在每个路由处理函数前验证用户是否具有该操作所需的权限。

  4. 服务层验证逻辑:在业务逻辑层编写函数来验证当前用户是否有权限执行特定操作。例如,检查用户的角色是否包含某个权限。

  5. 利用Guard增强安全性:使用NestJS的Guard机制,在控制器方法上应用自定义Guard来动态决定是否允许访问。例如,@CheckPermissions('update')装饰器。

  6. 前端与后端交互:确保API接口设计时明确标注所需权限,并在前端提示用户登录或升级权限。

  7. 测试:为所有涉及权限校验的部分编写单元测试,确保逻辑正确无误。

通过以上步骤,可以在NestJS项目中构建出一套灵活且安全的RBAC体系。

NestJS实战:基于角色的访问控制(RBAC)实现

在NestJS中实现RBAC(基于角色的访问控制)可以通过以下步骤完成:

核心实现步骤

  1. 定义角色枚举
// roles.enum.ts
export enum Role {
  User = 'user',
  Admin = 'admin',
  Moderator = 'moderator'
}
  1. 创建角色装饰器
// roles.decorator.ts
import { SetMetadata } from '@nestjs/common';
import { Role } from './roles.enum';

export const ROLES_KEY = 'roles';
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
  1. 实现角色守卫
// roles.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './roles.decorator';
import { Role } from './roles.enum';

@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();
    return requiredRoles.some((role) => user?.roles?.includes(role));
  }
}
  1. 在控制器中使用
// cats.controller.ts
@Controller('cats')
@UseGuards(JwtAuthGuard, RolesGuard)
export class CatsController {
  @Get()
  @Roles(Role.Admin, Role.Moderator)
  findAll() {
    return 'This action returns all cats';
  }
}
  1. 全局注册守卫
// main.ts
app.useGlobalGuards(new RolesGuard(new Reflector()));

用户实体示例

// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
import { Role } from '../auth/roles.enum';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;

  @Column('simple-array')
  roles: Role[];
}

注意事项

  1. 确保在JWT认证后才能使用角色守卫
  2. 数据库中的用户角色字段应为数组类型
  3. 可以根据需要调整角色权限级别
  4. 考虑使用缓存提高角色验证性能
回到顶部