rbac权限控制

RBAC和ACL

ACL

ACL权限控制是一种控制用户权限访问的手段,一个用户就对应了一个权限,讲起来比较抽象。
也就是说:我们现在有ABC三个接口,但是用户二只能访问A,不能让他看到B,但是用户三只有AB两个权限。(最常见的例子比如:学生管理系统,包括学生老师管理员三种角色)
我们用图来解释一下咱们的acl权限控制:
在这里插入图片描述
通过图我们能很直观的看出来ACL权限控制是做了件什么事。
通常我们使用ACL权限控制我们只需要建立三张表即可:
用户表,权限表以及权限和用户的中间表。

RBAC

现在我们有这样一个场景:现在有abc三个管理员,他们有aaa这个权限。现在我们新出了一个bbb权限,那么我们就要分别给abc三个管理员都加上bbb这个权限。这里只有三个管理员和一个新权限可能感觉不出来,那如果是一百个管理员和一百个权限,是不是这种方法就显得累赘。这时候我们就出现了RBAC这种新的权限控制方法。
RBAC也就是说在ACL中间加一层,先给每个用户分配角色,然后给每个角色分配权限,就像上面说的三个管理员和一权限,用ACL我需要添加三次,而RBAC这种方式只需要添加一次就够了。也就是只需要在权限表中吧bbb这个权限添加给管理员,这样abc三个管理员都有这个权限了。听起来还是有点抽象,我们还是看图:
在这里插入图片描述
这张图就更加直观的反应了我们RBAC权限控制的流程。
我们想要实现这个RBAC的控制流程我们就需要建立五张表:
用户,角色,权限以及用户角色关联表和角色权限关联表。

nest+mysql+prisma实现RBAC

这里就不介绍nest,mysql和prisma分别是啥了。简单来说:nest是一个nodejs的企业级开发框架,mysql就是数据库,prisma是操作数据库的orm可以让我们不用写sql用他们封装好的方法就能操作数据库的增删改查。

新建nest项目

pnpm i -g @nestjs/cli //全局安装nest脚手架
nest new rbac-test // 新建nest项目

然后安装需要的依赖,我们这里只需要安装prisma所需要的依赖就行了,为了方便调试,在安装一个swagger依赖。

pnpm install --save @nestjs/swagger
pnpm install prisma --save-dev

这里就不说怎么配置了详情可以看文档‘
nest集成prisma
nest集成swagger
集成完以后项目目录应该是这样的
在这里插入图片描述
然后我们可以在prisma文件夹下面的schema.prisma新建数据库

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = "prisma-client-js"
}

//provider 为mysql数据库
//url      为读取的数据源来源也就是你需要在目录中.env配置你的数据库连接信息
datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}
// 用户表
model User {
  id        String     @id @default(uuid())
  username  String
  password  String
  nickName  String?
  email     String
  phone     String?
  avatar    String
  createdAt DateTime   @default(now())
  updatedAt DateTime   @updatedAt
  roles     Role[]
  UserRole  UserRole[]
}

//角色表
model Role {
  id            String          @id @default(uuid())
  name          String
  createdAt     DateTime        @default(now())
  updatedAt     DateTime        @updatedAt
  user          User?           @relation(references: [id], fields: [userId])
  userId        String?
  UserRole      UserRole[]
  RoleAuthority RoleAuthority[]
  authorities   Authority[]
}
//用户角色关联表
model UserRole {
  userId    String
  roleId    String
  user      User     @relation(fields: [userId], references: [id])
  role      Role     @relation(fields: [roleId], references: [id])
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt

  @@id([userId, roleId])
}
//权限表
model Authority {
  id            String          @id @default(uuid())
  name          String
  createdAt     DateTime        @default(now())
  updatedAt     DateTime        @updatedAt
  Role          Role?           @relation(fields: [roleId], references: [id])
  roleId        String?
  RoleAuthority RoleAuthority[]
}
//权限角色关联表
model RoleAuthority {
  id          String    @id @default(uuid())
  roleId      String
  authorityId String
  createdAt   DateTime  @default(now())
  updatedAt   DateTime  @updatedAt
  Role        Role      @relation(fields: [roleId], references: [id])
  Authority   Authority @relation(fields: [authorityId], references: [id])
}

这里就不具体说怎么完成登录注册,就是使用jwt区分token。
我们可以在表中新建三个用户,两个角色和8个权限 具体的我们参考下面这个图
在这里插入图片描述
可以看看每个表:
在这里插入图片描述
这里的表就是按照上面的图所写的数据。
我们在nest里面生成aaa bbb 的一些crud,在nest中生成crud相当简单

nest g res aaa
nest g res bbb

这样就能生成aaa和bbb所有的crud接口。
我们现在就是要用权限去控制这些接口的访问。
管理员的角色有 aaa、bbb 的增删改查权限,而普通用户只有 bbb 的增删改查权限。
这里省略验证用户是否登录的过程。

新建一个自定义守卫

nest g guard permission --no-spec --flat

在这里插入图片描述
在app.module下面注册一下我们刚刚新建的自定义守卫。
然后我们导出一下user.module
在这里插入图片描述
然后我们在装饰器里面注入UserService:

import { CanActivate, ExecutionContext, Inject, Injectable } from '@nestjs/common';
import { Request } from 'express';
import { Observable } from 'rxjs';
import { UserService } from './user.service';

@Injectable()
export class PermissionGuard implements CanActivate {

  @Inject(UserService) 
  private userService: UserService;

  canActivate(
    context: ExecutionContext,
  ): boolean | Promise<boolean> | Observable<boolean> {

    console.log(this.userService);

    return true;
  }
}

然后在 userService 里实现查询 role 的信息的 service:


  async findRolesByIds(roleIds: string[]) {
    return this.prisma.role.findMany({
      where: {
        id: {
          in: roleIds
        }
      },
      include: {
        RoleAuthority: {
          include: {
            Authority: true
          }
        }
      }
    })
  }

然后我们改造一下PermissionGuard

@Injectable()
export class PermissionGuard implements CanActivate {

  @Inject(UserService)
  private userService: UserService;

  async canActivate(
    context: ExecutionContext,
  ): Promise<boolean> {
    const request: Request = context.switchToHttp().getRequest();

    if (!request.user) {
      return true;
    }
    const arr = []
    arr.push(request.user.roles)

    const roles = await this.userService.findRolesByIds(arr.map(item => item.id))

    const permissions: Authority[] = roles.reduce((total, current) => {
      total.push(...current.RoleAuthority.map(item => item.Authority));
      return total;
    }, []);

    console.log(permissions);

    return true;
  }
}

我们在请求头里面加上登录的token信息
在这里插入图片描述
然后访问接口就能看见该用户的权限列表
在这里插入图片描述
然后在自定义一个decorator:

export const  RequirePermission = (...permissions: string[]) => SetMetadata('require-permission', permissions);

然后我们在 BbbController 上声明需要的权限。
在这里插入图片描述
在 PermissionGuard 里取出来判断:
在这里插入图片描述

    const requiredPermissions = this.reflector.getAllAndOverride<string[]>('require-permission', [
      context.getClass(),
      context.getHandler()
    ])

可以看到打印了用户有的 permission 还有这个接口需要的 permission。
在这里插入图片描述
然后把权限列表和所需权限一对比,就达到了控制的效果
在这里插入图片描述

    for (let i = 0; i < requiredPermissions.length; i++) {
      const curPermission = requiredPermissions[i];
      const found = permissions.find(item => item.name === curPermission);
      if (!found) {
        throw new UnauthorizedException('您没有访问该接口的权限');
      }
    }

通过最开始的图李四是没有权限访问aaa接口的 我们测试一下试试
在aaa的get请求上加上
在这里插入图片描述
然后再apipost运行一下看看有没有这个接口的权限
在这里插入图片描述
发现是没有权限的。说明我们rbac权限控制成功了。

仓库:简单的rbac实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值