一、简述
本文讨论 RBAC1 模型的实现方案。该方案仅提供思路参考,在生产实践中请根据具体场景具体分析。
二、RBAC1 模型分析
RBAC1 是 RBAC(基于角色的访问控制)模型中的一种,它在 RBAC0 的基础上引入了角色间的继承关系。简单来说,就是给角色分成几个等级,每个等级的权限不同,从而实现更细粒度的权限管理。
例如,我们可以把一个公司的销售经理看作一个角色,销售经理又可以分为高级销售经理和初级销售经理,这两个角色就形成了一个等级关系。高级销售经理拥有的权限包括自己的权限和初级销售经理的所有权限,这就是角色的继承关系。
从上图我们可以看出,RBAC1 模型在 RBAC0 模型上多了继承的概念。我们只需在 RBAC0 模型库表设计基础上展现出继承关系即可。
RBAC1 模型中的角色继承又分为一般继承和受限继承。
- 一般继承:这种关系允许上下级角色之间互相继承,没有限制。例如,角色 A 可以拥有角色 B 的权限,角色 B 也可以拥有角色 A 的权限,同时角色 C 也可以拥有角色 B 的权限。
- 受限继承:这种关系要求角色只能从上到下继承,即下级角色只能拥有上级其中的一个角色,但是上级角色可以拥有多个下级角色。这种继承关系构成一种树形结构,实现角色间的单继承。
我们讨论的是受限继承,一般继承过于复杂而且在生产实践中采用的也比较少。
三、数据库如何表示继承关系
如上图所示,在角色的继承关系上,我们可以发现该关系的数据结构应该是树形结构。在数据库中表达树形结构通常有以下几种方案:
- 嵌套集模型(Nested Set Model):这是一种经典的表示树形结构的方法。在这种模型中,每个节点都有一个左边界和一个右边界值,通过这两个值可以确定节点的位置以及节点之间的层级关系。这种模型适合于读取频繁、更新不频繁的场景,但是插入、删除节点的操作比较复杂。
- 父子关系模型(Parent-Child Model):在父子关系模型中,每个节点包含一个指向其父节点的外键。这种模型简单直观,易于理解,适用于树的层级不太深的情况。但是在查询深层级的数据时,可能需要进行递归查询,效率较低。
- 路径枚举模型(Path Enumeration Model):在路径枚举模型中,每个节点都存储了到达根节点的完整路径。这种模型适用于读取操作频繁、更新操作相对较少的场景,但是路径存储可能会占用较多的存储空间,而且不易进行节点移动或重组。
- 闭包表模型(Closure Table Model):闭包表模型使用一个额外的表来存储节点之间的关系,每一行记录表示一个节点之间的直接或间接关系。这种模型适用于频繁更新的情况,但是需要额外的存储空间,并且对于大型树结构可能会导致闭包表的大小增长较快。
3.1 嵌套集模型
id | name | left | right |
---|---|---|---|
1 | 总经办 | 1 | 5 |
2 | 财务部 | 2 | 3 |
3 | 人力资源部 | 3 | 3 |
4 | 技术部 | 4 | 4 |
5 | 销售部 | 5 | 5 |
在嵌套集模型中,表中的 left(左边界)和 right(右边界)表示节点的位置和层级关系。例如,财务部的左边界是 2,右边界是 3;总经办的左边界是 1,右边界是 5,(1, 5) 的范围包括 (2, 3),则说明财务部是总经办的子部门,其余依次类推。
3.2 父子关系模型
id | name | parent_id |
---|---|---|
1 | 总经办 | NULL |
2 | 财务部 | 1 |
3 | 人力资源部 | 2 |
4 | 技术部 | 1 |
5 | 销售部 | 1 |
父子关系模型中,通过父 id 来表示层次关系。例如:财务部的 parent_id 是 1,总经办的 id 值为 1,那么说明财务部是总经办的子部门。
3.3 路径枚举模型
id | name | path |
---|---|---|
1 | 总经办 | /1 |
2 | 财务部 | /1/2 |
3 | 人力资源部 | /1/2/3 |
4 | 技术部 | /1/4 |
5 | 销售部 | /1/5 |
路径枚举模型中,通过路径来表示层次关系。例如:财务部的 path 是 /1/2,那么表示其父部门是 /1,而总经办的 path 是 /1,那么说明财务部是总经办的子部门。
3.4 闭包表模型
-
实体表
id name 1 总经办 2 财务部 3 人力资源部 4 技术部 5 销售部 -
关系表
id ancestor_id descendant_id 1 1 1 2 1 2 3 1 4 4 1 5 5 2 3
闭包表模型中,通过额外的关系表来维护层次关系。例如:关系表中 id 为 2 的这行数据,ancestor_id(父 id) 为 1,descendant_id(子 id) 为 2。那么,在实体表中 id = 1 则是 id = 2 的父节点(即总经办是财务部的父节点)
四、RBAC1 库表设计方案
父子关系模型具有简单直观,易于理解的特点,我们采用该模型进行库表设计。如有其他需求可结合上述几种树形表结构方案的特性进行设计。
五、RBAC1 模型处理流程
在上述获取角色的过程中,首先需要进行环路检测,这是因为我们采用的是父子关系模型设计的表结构。该模型需要递归的去获取所有角色,若表中存储的数据存在环路可能会出现死循环,所以在执行数据查询之前需要检测环路。
六、FAQ
-
环路检测可以有哪些方案?
可采用 DFS(深度优先搜索)、BFS(广度优先搜索)、拓扑排序、并查集、哈希表、快慢指针等方式
-
数据库中如何实现树形结构的查询?
第一种方案:可以采用自连接的方式,例如:
-- 这种方式不太灵活,比如下面的代码表明只有两层 -- 若层数不固定这种方式是无法满足的 SELECT id, role_name FROM tbl_role A INNER JOIN tbl_role B ON A.id = B.parent_id WHERE A.id = 1
第二种方案:使用数据库提供的方式,例如 MySQL 中可使用 WITH RECURSIVE,在 Oracle 中可以使用 CONNECT BY。
第三种方案:在业务层实现树形结构数据的查找