【前端】权限中心方案设计

615 阅读5分钟

(文章基于 vue2.0 环境进行阐述,参考设计思路可适用于全部前端场景)

RBAC 模型

RBAC ,基于角色的权限访问控制Role-Based Access Control):
RBAC的核心在于用户只和角色关联,而角色代表对了权限,是一系列权限的集合。
RBAC三要素:

  • 用户:系统中所有的账户
  • 角色:一系列权限的集合(如:管理员,开发者,审计管理员等)
  • 权限:菜单,按钮,数据的增删改查等详细权限。 优点:
  • 便于角色划分
  • 灵活的授权管理
  • 最小颗粒度授权

设计架构

权限分类

功能权限

访问权限:动态路由实现
菜单权限:基于路由表或者按钮极权限渲染
按钮权限:元素级操作权限

数据权限

接口请求操作,增删改查权限(本期需求)

权限优先级

通常来说【功能权限】>【数据权限】

举例说明:【浏览】/【点击】>【增】/【删】/【改】/【查】

基础架构图

流程图.jpg

按钮级权限架构

通常来说有两种实现方式:

  1. 基于角色对比方案,流程为:【操作模块需要的权限】-【权限中心】-【判断有无权限】-【是否有权限】 流程图 (2).jpg 优势:
  • 调用方便
  • 单条件验证(只需传入功能需要的角色即可)
  • 容易实现 劣势:
  • 控制权完全交给开发人员
  • 权限中心只能验证身份信息
  • 管理分散,不易排查问题
  • 不易与扩展
  • 无法做到多条件验证
  1. 基于描述语句方案,流程为:【操作模块需要的权限】-【权限中心】-【判断有无权限】-【是否有权限】 流程图 (3).jpg 优势:
  • 统一管理
  • 易与扩展
  • 多条件验证
  • 解耦,容易排查问题

劣势:

  • 调用参数相对繁琐
  • 调用依赖性强
  • 新同学学习成本高

数据级权限架构

暂时由后端接口统一控制,后期如有必要前端也需要加限制

权限中心结构

基本元素

Effect

权限结果:允许(Allow)和拒绝(Deny)

Action

操作行为:get、add、update、delete、view等

Resource

操作资源:<module>:<relative>:<instance>

  • Module ,资源所在模块
  • Relative,资源功能,如果不支持可用 * 代替
  • Instance,资源实例,如果不支持可用 * 代替 例如:
 {
   'resource': 'replay:play:dev_tools',
   'action': 'use'
 }

代表操作资源为【会话回放】-【会话播放页】-【开发者工具】的 use 权限

Condition

Effect 为 Allow 时的限制条件,可为多个 例如:

 {
     'effect': 'Allow',
     'resource': 'projects/setting:members:role',
     'action': 'update',
     'condition': {
          'Except': ['myself']
      }
 }

代表操作资源为【项目设置】-【成员管理】-【角色】的修改权限,但是不能修改自己

需求

原始需求表(举例)

功能模块功能细分操作权限超管管理员分析师
项目管理-查看✔️✔️✔️
项目管理-创建✔️
项目设置成员管理查看✔️✔️✔️
项目设置成员管理编辑「」用户名✔️「所有用户」✔️「所有管理员和分析师」✔️「仅自己」
项目设置成员管理修改或删除「」角色✔️「除自己以外的其他用户」✔️「除自己以外的其他管理员和分析师」

需求翻译

11111.jpg

补充说明:

  • Either: 至少满足其中一项条件,才允许操作
  • Exclude: 排除满足条件的操作
  • Either 和 Exclude 任意一个字段存在,则必须进行校验
  • 通常情况下 Exclude 和 Either 只存在一个
  • Exclude 和 Either 如果同时存在,Exclude 优先级高于 Either
  • 数据级现在由后端接口控制,后端同学需要关注
  • 功能级由前端界面控制,前端同学需要关注
  • 测试同学可根据此表格进行测试

落地实现

后端返回

请求方法

Get 请求

返回示例

 {
     "code": "00000",
     "data": {
         "Role": "superAdmin",
         "resources": [
             {
                 "id": 95,
                 "createAt": "0001-01-01T00:00:00Z",
                 "role": "superAdmin",
                 "resource": "projects:*:*",
                 "action": "add",
                 "exclude": ["myself"],
                 "either": [],
             },
             // ...
         ],
     },
     "message": "Success",
     "meta": {
     "time": "2022-03-10T10:43:33.251429786+08:00"
     }
 }

字段解释

aaa.jpg

前端调用

权限中心提供两个方法:

  1. $permission 获取某项具体权限
  2. $checkRole 检查当前用户角色是不是需要的
    (大多数情况下无需调用此方法,如 $permission 无法满足,可以用此方法补充,99%的情况下 $permission 方法已经能够满足)

方法实现

interface P {
  userId?: Number, // 要操作对象的 userId
  role?: String, // 要操作对象的 role
}

 /**
 * @description: 获取某项具体权限
 * @param {*} resource 对应【 需求翻译 】表格中的 resource 字段,需要查看
 * @param {*} action 对应【 需求翻译 】表格中的 action 字段,需要查看
 * @param {*} payload 在【 需求翻译 】表格中存在 condition 时,需要传递 payload 参数
 * @return {*} 结果
 */

Vue. prototype.$permission = async function(resource:string, action:string, payload: P): boolean {
    ... ...
}

 /**
 * @description: 检查当前用户角色是不是需要的
 * @param {*} roles 需要检查的角色数组,比如需要超管权限则为 ['superAdmin']
 * @return {*} 结果
 */

Vue.prototype.$checkRole = async function(roles: string[]):boolean {
    ... ...
}

参数说明

  • $permission 参数 | 说明 | 通俗理解 | 示例 | 必传 | 备注 | | ---------- | ------ | ---- | -------------------------------- | -- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | resource | 操作资源 | 在哪 | projects_setting:members:role | 是 | <module>:<relative>:<instance>组合,建议查阅【需求翻译】表格 | | action | 操作行为 | 干什么 | update | 是 | 建议查阅【需求翻译】表格 | | payload | 操作对象信息 | 操作谁 | { role: 'admin', userId: 123 } | 否 | | 在【需求翻译】表格中存在 condition 时,需要传递 payload 参数 涉及 myself 条件时,方法会强制校验 payload.userId 参数 涉及 superAdmin、admin 和 analyst 条件时,方法会强制校验 payload.role 参数
  • $checkRole
参数说明通俗理解示例必传备注
roles角色数组这个地方需要有哪些权限的人才能操作['superAdmin', 'admin']全部值:admin,superAdmin,analyst(超级管理员,管理员,分析师)

调用示例

示例1

此处需要超级管理员或者管理员权限

// 此处需要超级管理员或者管理员权限
const result = await this.$checkRole(['superAdmin', 'admin']);
console.log(result ) // true 或者 false

示例2

在【项目设置】-【成员管理】,获取【查看】 权限

// 在【项目设置】-【成员管理】,获取【查看】  权限
const effect = await this.$permission(
    'projects_setting:members:*',
    'view'
);
console.log(effect) // true 或者 false

示例3

在【项目设置】-【成员管理】,获取修改某个用户【角色】信息的 权限

// 在【项目设置】-【成员管理】,获取修改某个用户【角色】信息的 权限,
// 这个用户的信息是:用户ID为 123 角色为 admin
const effect = await this.$permission(
    'projects_setting:members:role',
    'update',
    { role: 'admin', userId: 123 }
);
console.log(effect) // true 或者 false

进一步加工

指令,组件等等,不限制