基于Nest.js的RBAC权限控制设计

您好, 如果喜欢我的文章或者想上岸大厂,可以关注公众号「量子前端」,将不定期关注推送前端好文、分享就业资料秘籍,也希望有机会一对一帮助你实现梦想

前言

对于后台管理系统,权限控制、千人千面是必须的,例如超级管理员可以看到所有的页面;普通用户能看到A、B页面;VIP用户可以看到A、B、C、D页面,诸如此类,这些业务的逻辑背后就是三种概念的设计。

  • 用户,最基本的用户,例如张三、李四、王五;
  • 角色,一个用户可以对应一个或多个角色,例如张三是普通用户+VIP的角色;
  • 权限,一个角色对应多个权限,例如VIP角色有查看、修改、增加的权限;超级管理员有查看、修改、增加、删除的权限;

以图来划分就像是这样的拆分关系:

接下来我们用nest来0~1实现一下一个系统的根基------------权限设计。

创建数据库

首先我们需要创建数据库,这里我们使用 mysql 数据库,我们执行以下语句创建数据库。

javascript 复制代码
CREATE DATABASE `nest-database` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

项目初始化

我们起一个nest项目,执行以下命令:

javascript 复制代码
nest new nest-project

然后安装数据库相关依赖,主要有typeormmysql2

javascript 复制代码
npm install --save @nestjs/typeorm typeorm mysql2

然后进行typeorm的相关配置,在app.module.ts中:

typescript 复制代码
import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { TypeOrmModule } from '@nestjs/typeorm'

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'nest-database',
      synchronize: true,
      logging: true,
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      poolSize: 10,
      connectorPackage: 'mysql2'
    })
  ],
  controllers: [AppController],
  providers: [AppService]
})

export class AppModule {}

然后我们进行表设计,一般rbac会有5张表,如下:

  1. 用户表,user,保存用户基本信息,如用户名、密码、邮箱;
  2. 角色表,role,保存角色基本信息,如角色名,角色code;
  3. 权限表,permission,保存权限基本信息,如权限名,权限code;
  4. 用户角色关联表,user_role_relation,用户角色关联表,用于记录用户和角色的关系;
  5. 角色权限关联表,role_permission_relation,角色权限关联表,用于记录角色和权限的关系;

领域模型图是这样的:

然后在nest中新建三张非关联表,并把关联的关系写一下:

user.entity.ts

typescript 复制代码
import {
  Column,
  CreateDateColumn,
  Entity,
  JoinTable,
  ManyToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn
} from 'typeorm'

import { Role } from './role.entity'

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

  @Column({
    length: 50
  })
  username: string

  @Column({
    length: 50
  })
  password: string

  @CreateDateColumn()
  createTime: Date

  @UpdateDateColumn()
  updateTime: Date

  @ManyToMany(() => Role)
  @JoinTable({
    name: 'user_role_relation', 
    joinColumn: {
      name: 'userId',
      referencedColumnName: 'id', 
    },
    inverseJoinColumn: {
      name: 'roleId',  
      referencedColumnName: 'id', 
    },
  })
  roles: Role[];
}

我们在User表中定义了rule字段,通过连接到user_role_relation表,连接判断关系是user.id === userRoleRelation.userIdrole.id === userRoleRelation.roleId,才会将满足条件的Role记录插入到User中,自动形成关联关系。

role.entity.ts

typescript 复制代码
import {
  Column,
  CreateDateColumn,
  Entity,
  JoinTable,
  ManyToMany,
  PrimaryGeneratedColumn,
  UpdateDateColumn
} from 'typeorm'

import { Permission } from './permission.entity'

@Entity()
export class Role {
  @PrimaryGeneratedColumn()
  id: number

  @Column({
    length: 20
  })
  name: string

  @CreateDateColumn()
  createTime: Date

  @UpdateDateColumn()
  updateTime: Date

  @ManyToMany(() => Permission)
  @JoinTable({
    name: 'user_role', 
    joinColumn: {
      name: 'roleId', 
      referencedColumnName: 'id', 
    },
    inverseJoinColumn: {
      name: 'permissionId', 
      referencedColumnName: 'id', 
    },
  })
  permissions: Permission[]
}

这张角色表的permissions字段原理一样,通过连接到role_permission_relation表,连接判断关系是role.id === rolePermissionRelation.roleIdpermission.id === rolePermissionRelation.permissionId,才会将满足条件的Permission记录插入到Role中,自动形成关联关系。

最后是权限表,没有关联关系,直接记录有哪些权限即可。

permission.entity.ts

typescript 复制代码
import { Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'

@Entity()
export class Permission {
  @PrimaryGeneratedColumn()
  id: number

  @Column({
    length: 50
  })
  name: string

  @Column({
    length: 100,
    nullable: true
  })
  desc: string

  @CreateDateColumn()
  createTime: Date

  @UpdateDateColumn()
  updateTime: Date
}

到这里表的设计就完成了,我们初始化一批数据,写一个init服务。

typescript 复制代码
async initData() {
  const user1 = new User()
  user1.username = '张三'
  user1.password = '111111'

  const user2 = new User()
  user2.username = '李四'
  user2.password = '222222'

  const user3 = new User()
  user3.username = '王五'
  user3.password = '333333'

  const role1 = new Role()
  role1.name = '管理员'

  const role2 = new Role()
  role2.name = '普通用户'

  const permission1 = new Permission()
  permission1.name = '新增 aaa'

  const permission2 = new Permission()
  permission2.name = '修改 aaa'

  const permission3 = new Permission()
  permission3.name = '删除 aaa'

  const permission4 = new Permission()
  permission4.name = '查询 aaa'

  const permission5 = new Permission()
  permission5.name = '新增 bbb'

  const permission6 = new Permission()
  permission6.name = '修改 bbb'

  const permission7 = new Permission()
  permission7.name = '删除 bbb'

  const permission8 = new Permission()
  permission8.name = '查询 bbb'

  role1.permissions = [
    permission1,
    permission2,
    permission3,
    permission4,
    permission5,
    permission6,
    permission7,
    permission8
  ]

  role2.permissions = [permission1, permission2, permission3, permission4]

  user1.roles = [role1]

  user2.roles = [role2]

  await this.entityManager.save(Permission, [
    permission1,
    permission2,
    permission3,
    permission4,
    permission5,
    permission6,
    permission7,
    permission8
  ])

  await this.entityManager.save(Role, [role1, role2])

  await this.entityManager.save(User, [user1, user2])
}

去浏览器或者postman调一下initData接口,表里就有数据了。

这样的一批人员权限关系也就设计好了,验证一下数据没有问题。

有了最基本的权限设计接下来就可以去做注册、登录、JWT拦截相关的开发了,这里之前已经讲过,直接跳转到这两篇文章参考即可。

Midway.js零到一实现登录、注册、鉴权功能

伪造请求怎么办?看这篇就够了

如果喜欢我的文章或者想上岸大厂,可以关注公众号「量子前端」,将不定期关注推送前端好文、分享就业资料秘籍,也希望有机会一对一帮助你实现梦想。

相关推荐
n***84076 分钟前
Spring Boot(快速上手)
java·spring boot·后端
gongzemin6 分钟前
约课小程序增加候补功能
前端·微信小程序·小程序·云开发
西西西西胡萝卜鸡10 分钟前
徽标(Badge)的实现与优化铁壁猿版(简易版)
前端
Wgrape15 分钟前
一文了解常见AI搜索方案的代码实现
人工智能·后端
王大宇_19 分钟前
虚拟列表从入门到出门
前端·javascript
小兔崽子去哪了22 分钟前
Docker部署ZLMediaKit流媒体服务器并自定义配置指南
java·后端·容器
程序猿小蒜23 分钟前
基于springboot的人口老龄化社区服务与管理平台
java·前端·spring boot·后端·spring
aiopencode26 分钟前
iOS 开发者工具推荐,构建从调试到性能优化的多维度生产力工具链(2025 深度工程向)
后端
iOS开发上架哦44 分钟前
iOS APP 抓包全流程解析,HTTPS 调试、网络协议分析与多工具组合方案
后端
用户21411832636021 小时前
Google Nano Banana Pro图像生成王者归来
前端