前言
本文只提供一些想法并没有demo项目
权限管理是所有成熟系统的都会涉及的一个重要组成部分,主要目的是对不同的人访问资源进行权限的控制,避免因权限控制缺失或操作不当引发的风险问题,如操作错误,隐私数据泄露等问题。
RBAC权限模型
本文针对RBAC权限模型进行讨论
RBAC是什么?
RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
1.0级 用户、角色、权限
基础模型,用户角色权限之间都是多对多的关系
2.0级 权限分级
公司->部门->小组
公司总经理,部门经理,小组长的权限是不一样的
同级之间,开发部门经理和销售部门经理的权限也是不一样的
2.1级 权限继承
这个人是小组长,那他必然有一个归属部门,必然有一个归属公司。
假设
【研发部门】的基础权限是可以申请测试手机
【公司】 的基础权限是可以使用门禁卡开门
当给一个用户赋值【研发部门小组长】的时候会自动带上部门和公司的基础权限。即使【研发部门小组长】这个权限点不包含门禁卡开门。但是这个人会继承公司的基本权限
2.3级 权限互斥,权限上限
场景1
比如购置设备申请表需要,【申请人】、【采购部长】、【财务部长】 三个人签字。
公司设置这个签字规则就是为了互相监督。角色分配上不允许一个人同时有【申请人】、【采购部长】、【财务部长】三个角色
场景2
假设系统一共有N个功能
角色1->功能点1
角色2->功能点2
…
角色2->功能点n
超级管理员->功能点1~n
此时如果一个人的角色不是超管,但是给他赋予了除超管外的全部角色。那么他相当于一个伪超管。当权限系统越发复杂的情况下,很容易产生 角色1+角色2 = 角色3这种情况,所以要添加角色互斥或角色上限的逻辑
前端实现权限
RBAC权限模型说了这么多角色逻辑,其实跟前端毫无关系,或者说可以毫无关系。这取决于前端是否实现业务逻辑。
先说明一点,我个人倾向于前端避免业务逻辑,以下的权限实现基于此前提。
如果前端代码掺杂了很多业务逻辑,那么前端的代码复用性和扩展性会有所降低。
1.复用性 我为公司人力管理系统搭建了一个vue3的前端项目里面【40%业务逻辑代码】【60%数据交互展示代码】,这时候又来了一个需求需要为库房管理系统搭建一个前端项目。两个项目虽然后台需求逻辑不同,但是ui交互操作几乎相同,但是你会发现我们只有【60%数据交互展示代码】可以复用。
2.维护性 如果实现了业务逻辑,随着业务逻辑的升级拓展功能会持续膨胀,如果超过了架构设计的拓展之外的功能就很难优雅的实现,导致项目维护成本持续增长,直到项目推翻重构,但是如果前端只负责数据展示和操作,只有在展示的数据类型和交互方式发生变化时,才涉及到系统的扩展性。
前端权限需求
【用户】和【角色】的逻辑行为由后端实现,前端只需要实现【权限】的行为
我们已商品管理系统举例
前后端交互的基本手段就是接口,常规下不同的功能必然是不同的接口,放飞自我的后端暂不讨论
根据接口来判断权限,用户登录后返回用户有权限的全部接口。
//前端获取的权限信息
const roleSet = new Set(['role','goods'])
场景1-菜单控制1.0
用户能不能看见这个页面,超管有系统管理页面,普通用户没有
非静态页面必定存在获取数据的接口,可以根据此接口权限判断是否展示此菜单页面。
例如
系统两个页面 权限管理、商品列表。
超管权限role ,goods
http://xxx.com/goods; // 商品信息
http://xxx.com/role; // 权限信息
//前端获取的权限信息
const role = new Set(['role','goods'])
普通用户只有goods
//前端获取的权限信息
const role = new Set(['goods'])
有role接口权限显示权限管理,有goods接口权限才显示商品列表。
const hasGoodsMenu = () => {
role.has('goods')
}
const hasRoleMenu = () => {
role.has('role')
}
这样普通用户就看不到商品列表。
场景2-页面控制1.0
同一个页面,不同用户看到的数据不一样。
存在两种情况
第一种数据隔离:用户A的购买记录和用户B的购买记录,两个人的数据是不互通的。这种功能一般由后端独立实现。
第二种数据权限:商品详情页面,用户只能看见商家编辑的商品信息,超管还可以看见商品的历史修改记录,什么时间变过价格,谁修改的等。若需要前端区分可以让不同用户使用不同的接口来获取数据。
if(role.has('admin/goods')){
return 'admin/goods'