前端数据级权限到底怎么玩

定义

RBAC权限体系大家都很清楚了,一般平时的后台管理系统都会用到,github上也有大把的权限模版项目,但都基本只支撑到角色这个颗粒度(这里指的是前端实现上)。那么在某些场景下就不会太适用,比如一个集团公司,有10个子公司,总部的会计要对子公司的财务数据进行工作,假设一个人负责一个子公司的数据,需要10个角色就可以完成分配。看起来很理想,但实际上情况要比这复杂的多。

  1. 小A会计离职,小B则需要负责两个子公司的数据
  2. 会计组来了卷王他需要负责5个子公司的数据
  3. 会计部长需要看到全部子公司的数据
  4. 子公司的增减
  5. ...

看到这里大家会说不就是多建几个角色的事情吗,但从数学的角度来讲n个公司会有多少这种角色呢,答案是2^n个,考虑到空集无实际意义,也就是2^n-1个角色的可能,这就容易造成前端角色爆炸,与角色绑定一起的相关判断也会变得很难维护。有人把数据级权限认为就是一个table后端返回不同的数据就完事,但实际上应该要控制到页面的资源级别。

作者认为颗粒度应该到某个图片/字段的可见性控制、表单提交的校验逻辑、甚至一些交互的顺序都应根据权限配置来进行控制

基本概念

作者给出的方案是在权限下面新增一个岗位职能的概念。来个例子:大家都是会计的角色,分配的菜单与按钮都一致,但操作的数据完全不一样,换句话说就是角色负责菜单、按钮,而岗位职能负责具体能看到并能操作的数据来源与字段。(下文岗位职能统称为岗位)

话不多说直接上数据结构

json 复制代码
[
    {
      "httpUrl": "",
      "icon": "ep:aim",
      "levelId": 1,
      "menuId": 2713021,
      "menuName": "菜单-46",
      "menuParentId": 0,
      "systemCode": "A-SYS",
      "children": [
        {
          "httpUrl": "",
          "icon": "",
          "levelId": 2,
          "menuId": 2713022,
          "menuName": "菜单-47",
          "menuParentId": 2713021,
          "systemCode": "A-SYS",
          "children": [
            {
              "httpUrl": "",
              "icon": "",
              "levelId": 3,
              "menuId": 2713025,
              "menuName": "菜单-48",
              "menuParentId": 2713022,
              "systemCode": "A-SYS",
              "pressButtons": [
                {
                  "Name": "新增",
                  "code": "BTN-NEW"
                },
                {
                  "Name": "编辑",
                  "code": "BTN-EDIT"
                }
              ],
              "permissions": [
                {
                  "jobId": "810000197205203382",
                  "jobName": "岗位职能-A"
                },
                {
                  "jobId": "630000201612261761",
                  "jobName": "岗位职能-B"
                }
              ]
            },
            {
              "httpUrl": "",
              "icon": "",
              "levelId": 3,
              "menuId": 2803012,
              "menuName": "菜单-49",
              "menuParentId": 2713022,
              "systemCode": "A-SYS",
              "pressButtons": [
                {
                  "Name": "编辑",
                  "code": "BTN-EDIT"
                }
              ],
              "permissions": [
                {
                  "jobId": "360000199110139912",
                  "jobName": "岗位职能-C"
                },
                {
                  "jobId": "45000019960318289X",
                  "jobName": "岗位职能-D"
                }
              ]
            },
            {
              "httpUrl": "",
              "icon": "",
              "levelId": 3,
              "menuId": 2713026,
              "menuName": "菜单-50",
              "menuParentId": 2713022,
              "systemCode": "A-SYS",
              "pressButtons": [
                {
                  "Name": "导出",
                  "code": "BTN-EXPORT"
                }
              ],
              "permissions": [
                {
                  "jobId": "330000197309154429",
                  "jobName": "岗位职能-E"
                },
                {
                  "jobId": "540000201904112854",
                  "jobName": "岗位职能-F"
                }
              ]
            }
          ]
        },
        {
          "httpUrl": "",
          "icon": "fa fa-calculator",
          "levelId": 2,
          "menuId": 2713023,
          "menuName": "菜单-54",
          "menuParentId": 2713021,
          "systemCode": "A-SYS",
          "children": [
            {
              "httpUrl": "",
              "hidden": true,
              "icon": "",
              "levelId": 3,
              "menuId": 2773015,
              "menuName": "菜单-55",
              "menuParentId": 2713023,
              "systemCode": "A-SYS"
            }
          ]
        }
      ]
    },
    {
      "httpUrl": "",
      "icon": "",
      "levelId": 1,
      "menuId": 1110026,
      "menuName": "菜单-59",
      "menuParentId": 0,
      "systemCode": "A-SYS",
      "children": [
        {
          "httpUrl": "",
          "icon": "",
          "levelId": 2,
          "menuId": 1110027,
          "menuName": "菜单-60",
          "menuParentId": 1110026,
          "systemCode": "A-SYS",
          "pressButtons": [
            {
              "Name": "编辑",
              "code": "BTN-EDIT"
            }
          ],
          "permissions": [
            {
              "jobId": "410000198705127101",
              "jobName": "岗位职能-C"
            },
            {
              "jobId": "210000197008073054",
              "jobName": "岗位职能-D"
            }
          ]
        }
      ]
    }
  ]

可以看到岗位信息作为一个数组反映了当前人员当前菜单所能操作的页面内容权限的集合。那么就以这个森林形式的树形数据结构作为数据级权限控制的配置依据

这里需要注意的是按钮是挂载到角色上面,因为作者在实际工作中发现按钮一般作为功能的发起点,通常上线之后就会固定好使用人群的范围,变动的概率不高。对于页面中其他资源的控制,变动频繁的可以放在岗位下面,反之可以放角色下面

permissions字段反映了当前人员在当前页面所能看到的页面资源与操作数据的范围;假设页面进入时默认选择了"岗位职能-C",那么可以看到"岗位职能-C"控制下的页面资源、后端根据此岗位id所返回的数据以及前端开发根据此岗位ID进行的特殊逻辑处理。至于怎么切换到"岗位职能-D",页面加一个select的选择框就行,并不会造成产品上的冗余,反而有利于提高用户的使用感知度,例如起一个相关的名字"A自公司的账单审查"。因为产品也大概率会加一个字段用于区分不同的数据来源,而现在只需要切换到相应的岗位就能确定使用人员看到与操作的数据权限范围

总结

我们总结出前端数据级权限控制的基本概念:页面资源甚至逻辑都应纳入权限控制,分离出岗位的概念用于前端控制实现

作者主要针对的是前端实现,因为作者看到很多系统明明在表结构上已经做到资源级的权限控制,但给到前端的依然是只有角色一个概念在玩,导致出现很多冗余的判断区分,维护起来也很麻烦。如果把资源控制相关的内容全部塞到角色下面,依然也是茫茫多的判断。只针对岗位这个概念上做判断,逻辑上会清晰很多

相关推荐
twins352043 分钟前
解决Vue应用中遇到路由刷新后出现 404 错误
前端·javascript·vue.js
qiyi.sky1 小时前
JavaWeb——Vue组件库Element(3/6):常见组件:Dialog对话框、Form表单(介绍、使用、实际效果)
前端·javascript·vue.js
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript
安冬的码畜日常1 小时前
【D3.js in Action 3 精译_027】3.4 让 D3 数据适应屏幕(下)—— D3 分段比例尺的用法
前端·javascript·信息可视化·数据可视化·d3.js·d3比例尺·分段比例尺
l1x1n02 小时前
No.3 笔记 | Web安全基础:Web1.0 - 3.0 发展史
前端·http·html
昨天;明天。今天。2 小时前
案例-任务清单
前端·javascript·css
zqx_73 小时前
随记 前端框架React的初步认识
前端·react.js·前端框架
惜.己3 小时前
javaScript基础(8个案例+代码+效果图)
开发语言·前端·javascript·vscode·css3·html5
什么鬼昵称4 小时前
Pikachu-csrf-CSRF(get)
前端·csrf
长天一色4 小时前
【ECMAScript 从入门到进阶教程】第三部分:高级主题(高级函数与范式,元编程,正则表达式,性能优化)
服务器·开发语言·前端·javascript·性能优化·ecmascript