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

定义

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自公司的账单审查"。因为产品也大概率会加一个字段用于区分不同的数据来源,而现在只需要切换到相应的岗位就能确定使用人员看到与操作的数据权限范围

总结

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

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

相关推荐
m0_5485147714 分钟前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
AndrewPerfect15 分钟前
xss csrf怎么预防?
前端·xss·csrf
Calm55018 分钟前
Vue3:uv-upload图片上传
前端·vue.js
浮游本尊22 分钟前
Nginx配置:如何在一个域名下运行两个网站
前端·javascript
m0_7482398323 分钟前
前端bug调试
前端·bug
m0_7482329225 分钟前
[项目][boost搜索引擎#4] cpp-httplib使用 log.hpp 前端 测试及总结
前端·搜索引擎
新中地GIS开发老师30 分钟前
《Vue进阶教程》(12)ref的实现详细教程
前端·javascript·vue.js·arcgis·前端框架·地理信息科学·地信
m0_7482495433 分钟前
前端:base64的作用
前端
html组态39 分钟前
web组态可视化编辑器
前端·物联网·编辑器·web组态·组态·组态软件
~央千澈~1 小时前
如果你的网站是h5网站,如何将h5网站变成小程序-除开完整重做方法如何快速h5转小程序-h5网站转小程序的办法-优雅草央千澈
前端·apache