现在web项目基本都是前后端分离的了,所以也是基于这种架构去思考权限管理的设计。
说实话,没做过很复杂的项目,所以考虑的细节有限,写这个也是为了让自己捋清楚思路,好下手写代码,而且代码的逻辑经得起推敲,不至于过段时间自己都看不懂了,还没地方找思路。
一个好的权限管理模块,我觉得最重要的点是:
- 管理者能够清晰的对权限进行管理,也就是那个管理界面,是符合正常人的思维的。
- 权限校验的性能要高,不能因为这个对整个系统的性能产生太大的影响
web 的权限大概分两块,一块时后端的接口权限,另一块是前端页面的权限
常见的权限模型
ACL
按照人最直接的思维,直接给用户授予权限,这种方式很直接,很好理解,用户有什么权限,一目了然。而且查询性能也很高,直接根据用户id就能查到,没有其他弯弯绕绕的。
但是,这种方式,让管理者很头痛,每个用户都得给他授权,工作量很大。
RBAC
这种模型就是解决ACL存在的问题,因为体积最大的一群用户,往往都具有一样的权限,比如我们作为淘宝的普通用户,我们在注册的时候,就被划为一个叫“普通用户”这样的角色。
管理者只要把“普通用户”这个角色的权限设置好,以后每个新来的,直接给他赋予这个角色即可,以后对权限的增删改查都只需要在角色上进行一次即可,所有用户的权限都一并修改了。大大减小工作量。
这是目前最常用的模型。但它也是一样有缺点的:
- 灵活性不够。这里就不展开了
- 其次校验权限的时候也比较麻烦,需要根据用户id 去查角色,然后再查角色具有的权限,才知道用户是否具有某个权限。实际应用中,一个用户还可能具有多个角色,角色也可能还有继承关系。校验权限的时候弯弯绕绕很多,设计不好很可能影响系统的性能,因为用户的每次访问都是要鉴权的。
那么RBAC和ACL结合起来一起用是不是也可以呢?应该是可以的。
但是,这样做没有很大的好处(无非减少一些角色而已,并没有解决RBAC的问题),反倒增加了权限系统的设计复杂性,所以不提倡结合设计,纯粹的RBAC就已经能够满足大多数场景了。
基于RBAC设计权限模块
RBAC的基本概念(自己想的,不一定是通用概念)有:
- 资源:权限管理的最小单位,不可再分
- 角色:资源的聚合体,角色之间可以继承,所以角色可以同时拥有资源和子角色
- 角色组:多个角色的聚合体,不一定非要有
- 用户:权限的拥有者,可以拥有多个角色和角色组
上面这几个概念里,我认为角色的继承是最为复杂的,比如说下面几种情况(以下字母均表示具体的角色):
- A继承自B,B继承自C,那么A同样拥有C的权限,这是最常见也好理解的。
- A继承自B,也继承自C,B和C的关系不明确。那么A的权限是B和C的聚集,也还好理解。
- A继承自B,B继承自C,C又继承自A,死循环。这是不允许的。所以角色之间是需要拥有等级关系的,C的等级低,不允许继承自A。
继承与角色组
角色之间可以继承无疑是对权限管理系统的设计增加了很大的复杂性。
但是如果不使用继承的话,管理员的工作量会大不少,比如:权限A和权限B所拥有的资源非常相似,且都拥有10几个资源,而A比B仅仅多了一两个资源,如果不允许继承,那么管理员每次新建角色时,就得照抄一遍B的资源,然后再增加额外的资源。这样工作量也不小。也不利于后期权限的整体变更。
继承又分单继承和多继承。
多继承其实和角色组是一个意思,角色组就是继承了多个角色嘛。
上面提到角色之间需要拥有等级,多继承容易使等级混乱,举个例子(以下字母代表具体的角色名):
- A继承自C,B也继承自C,那么可以认为,A,B都是C的上级,而A和B可以认为是平级的(类似于兄弟部门)。
- A继承自C和D,B继承自C,那么就不能认为A和B是平级了。至少一眼看过去不那么明显了。
所以,我倾向于让角色只允许单继承,多继承由角色组去实现。
角色组和角色的区别:
- 角色组是角色的集合,从概念上说,比角色高一个等级
- 角色组仅仅由角色组成,不可以拥有资源,只可以通过角色间接拥有资源
- 角色可以直接拥有资源,也可以继承自其他角色
- 通常,一个角色至少直接拥有一个资源,否则它更应该被认为是角色组
角色组
因为我们没有赋予业务背景,所以不好理解角色组存在的意义。如果赋予业务背景,例如我们常见的组织架构权限控制系统,那么角色组就可以理解为部门和职位了。
所以,角色组的存在更多是为了和业务层面对接。它屏蔽了资源,不让外面的成员能够直接感知资源的存在。
组织架构是明显具有上下级关系的,所以角色组也需要具有上下级关系,否则无法构成一个完整的组织架构。我试想过再独立建一个部门表来维护上下级关系,但始终都是要赋予他们对应的权限,而这个赋予的方式,也就是角色组。
所以角色组的结构是树结构
树结构是自上而下的,那么每个角色组应该设计为有且仅有一个上级,除了根节点。这个特点对于权限设计会简单很多。
综上总结角色组的特点:
- 由角色组成,不能直接拥有资源
- 具有上下级关系
- 有且仅有一个上级
授权功能
一个很常见的功能,部门主管可以给部门内人员授权,并且可以新建子部门和职位。
假设A部门下有个B部门,要在B部门下新增一个网络工程师的职位,那么能给这个职位授予的权限是B部门所包含的,理论上可以等于B部门。因为部门经理可以囊括部门所有的权限。
而授权功能本身是个敏感的权限,仅有系统管理员可以给予,是个特殊的存在,比如A部门的部门经理拥有授权的权限,但是不能给子部门的部门经理下发授权功能。
所以授权功能该有的特点是:
- 操控的元素是角色,而不是角色组
- 新增角色组仅能添加上级已有的权限
- 授权功只有系统管理员可以下发,其余人员均不可以下发
- 不能直接给人员授权,只能通过将人员拉进角色组的方式授权
组织架构调整
这是个麻烦的事,组织架构调整,分几种情况:
-
A部门下的子部门B整体划到C部门下;能进行这个操作的人一定是同时有权限操控A部门和C部门的人。为了最小限度影响到下面的人的权限,A部门的权限保持不变,C部门会增加B部门所包含的权限,B部门的权限不变。
-
A部门下新增一个子部门C,原来的B部门不再直接挂到A部门,而是挂到C部门下;刚创建时,C部门会等于B部门的权限,然后再根据需要给C部门授权。
-
A部门整体被裁;被裁的A部门及其子节点必须先清空人员,才允许由上级单位去删除
-
A部门被回收某个权限;A部门及其子节点都会被遍历,收回该权限。
以上所有受影响的子节点,都得相应变动自己的上下级关系。
角色的继承
角色由于可以单继承,所以必须具有等级关系,否则会造成循环继承的死循环。
上面也讲了,允许继承是为了减少授权的工作量:
- 重复的权限,继承即可直接获取,不需要管理员多次重复选择
- 修改角色的权限,所有直接或间接继承自该角色的所有角色和角色组都将被影响
角色的结构其实是个复杂链表,而且他的构建顺序是先有子节点再有父节点
继承使得整个权限模型的设计复杂了非常非常多。增删改查的难度的大了非常多。但是没有继承,对于资源多的系统,管理上又会增加很多工作量,下面分析下增删改查的要求:
增
这个最简单了,直接把想要的资源添加进来,看到合适的角色可以继承一下。不会对已有的角色造成影响
删
很麻烦,因为根据继承的特点,所有直接或间接继承了该角色,和包含了该角色的角色组,都会跟着变动。但是,我并不知道哪些角色直接或间接继承自它,以及哪些角色组包含了它。
链表的特点就是如此,即使使用双链表记录父节点信息,也一样非常麻烦,因为爷节点爷收到影响,往上还有祖宗节点。所以,不管如何,这都是牵一发而动全身的事
改
和删一样,都是牵一发而动全身
查
继承带来的问题是,查询一个用户是否有权限访问某个资源,需要经过以下几步:
- 查询用户所属的角色组
- 查询角色组包含的角色
- 查询各个角色下是否包含该资源
其实是一个很影响性能的过程,因为用户的每一次访问都需要这个查的过程。
以上分析,似乎得出一个结论,为了减轻管理人员的工作量,增加继承的概念,却给系统设计带来了极大的难度。但是真的就不可行吗?