权限系统与RBAC模型概述
1.RBAC简介
RBAC(Role-Based Access Control )基于角色的访问控制。
本文参考资料。
在20世纪90年代期间,大量的专家学者和专门研究单位对RBAC的概念进行了深入研究,先后提出了许多类型的RBAC模型,其中以美国George Mason大学信息安全技术实验室(LIST)提出的RBAC96模型最具有系统性,得到普遍的公认。 RBAC认为权限的过程可以抽象概括为:判断【Who是否可以对What进行How的访问操作(Operator)】这个逻辑表达式的值是否为True的求解过程。 即将权限问题转换为Who、What、How的问题。who、what、how构成了访问权限三元组。
RBAC 基于角色的访问控制,用户有m个, 资源有n个, 如果用户直接和资源绑定, 有o(mn)条记录, 如果要对某一个用户的某几个资源做修改,或者对某几个资源做修改,或者删除创建几个用户。用户与资源是多对多关系。参照现实,权限往往与职位而不是个人挂钩。这一个个职位,就像是角色。
另一种解释是我们每一个人都有多个不同的角色,上学时是学生,在家里是孩子,网络上是网友。不同的角色有不同的权限,比如:学生可以上学。
RBAC支持公认的安全原则:最小特权原则、责任分离原则和数据抽象原则。
- 最小特权原则得到支持,是因为在RBAC模型中可以通过限制分配给角色权限的多少和大小来实现,分配给与某用户对应的角色的权限只要不超过该用户完成其任务的需要就可以了。
- 责任分离原则的实现,是因为在RBAC模型中可以通过在完成敏感任务过程中分配两个责任上互相约束的两个角色来实现,例如在清查账目时,只需要设置财务管理员和会计两个角色参加就可以了。
- 数据抽象是借助于抽象许可权这样的概念实现的,如在账目管理活动中,可以使用信用、借方等抽象许可权,而不是使用操作系统提供的读、写、执行等具体的许可权。但RBAC并不强迫实现这些原则,安全管理员可以允许配置RBAC模型使它不支持这些原则。因此,RBAC支持数据抽象的程度与RBAC模型的实现细节有关。
最小特权原则保证了角色权限范围的合理性。(角色权限范围过大或者过小,都会导致角色绑定的用户权限不合理,比如需要绑定过多且无必要的角色,或者绑定的角色权限要么超过了该用户理应拥有的权限,要么少于理应拥有的权限)
RBAC96是一个模型族,其中包括RBAC0~RBAC3四个概念性模型。
1、基本模型RBAC0定义了完全支持RBAC概念的任何系统的最低需求。
2、RBAC1和RBAC2两者都包含RBAC0,但各自都增加了独立的特点,它们被称为高级模型。
RBAC1中增加了角色分级的概念,一个角色可以从另一个角色继承许可权。
RBAC2中增加了一些限制,强调在RBAC的不同组件中在配置方面的一些限制。
3、RBAC3称为统一模型,它包含了RBAC1和RBAC2,利用传递性,也把RBAC0包括在内。这些模型构成了RBAC96模型族。
@startuml
RBAC0 <|-- RBAC1
RBAC0 <|-- RBAC2
RBAC1 <|-- RBAC3
RBAC2 <|-- RBAC3
RBAC0 : 包含用户、角色、权限
RBAC1 : 增加了角色分级(继承关系)
RBAC2 : 增加了角色的访问控制
RBAC3 : 包含了RBAC0、RBAC1、RBAC2的全部内容
@enduml
2.RBAC模型简述
2.1 RBAC0
RBAC0的模型中包括用户(U)、角色(R)和许可权(P)等3类实体集合。
RABC0权限管理的核心部分,其他的版本都是建立在0的基础上。
@startuml
note "RBAC(Role-Based Access Control) 基于角色的访问控制\nRBAC0是RBAC的核心,主要有四部分组成:\n1、用户(User)\n2、角色(Role)\n3、许可(Permission)\n4、会话(Session)" as N1
User "0..*" - "0..*" Role
User -- Session
Role -- Session
Role "0..*" - "0..*" Permission
Permission - Object
Permission - Operation
class Permission {
- ops : Operation
- ops : Object
}
@enduml
RBAC0定义了能构成一个RBAC控制系统的最小的元素集合。
在RBAC之中,包含用户users(USERS)、角色roles(ROLES)、目标objects(OBS)、操作operations(OPS)、许可权permissions(PRMS)五个基本数据元素,此模型指明用户、角色、访问权限和会话之间的关系。
每个角色至少具备一个权限,每个用户至少扮演一个角色;可以对两个完全不同的角色分配完全相同的访问权限;会话由用户控制,一个用户可以创建会话并激活多个用户角色,从而获取相应的访问权限,用户可以在会话中更改激活角色,并且用户可以主动结束一个会话。
用户和角色是多对多的关系,表示一个用户在不同的场景下可以拥有不同的角色。
例如项目经理也可以是项目架构师等;当然了一个角色可以给多个用户,例如一个项目中有多个组长,多个组员等。
这里需要提出的是,将用户和许可进行分离,是彼此相互独立,使权限的授权认证更加灵活。
角色和许可(权限)是多对多的关系,表示角色可以拥有多分权利,同一个权利可以授给多个角色都是非常容易理解的,想想现实生活中,职位的级别不同的权限的情景,其实这个模型就是对权限这方面的一个抽象,联系生活理解就非常容易了。
在用户与资源中间增加了角色的概念,使得用户与资源解耦,方便管理用户和资源,已经他们两者的绑定关系。
2.2 RBAC1
RBAC1,基于RBAC0模型,引入角色间的继承关系,即角色上有了上下级的区别,角色间的继承关系可分为一般继承关系和受限继承关系。一般继承关系仅要求角色继承关系是一个绝对偏序关系,允许角色间的多继承。而受限继承关系则进一步要求角色继承关系是一个树结构,实现角色间的单继承。
这种模型合适于角色之间的层次明确,包含明确。
RBAC0引入角色概念,使得用户与资源解耦。 用户与资源不再具有直接关系, 他们的直接关系都是角色。角色的可维护性决定了用户和资源的可维护性(他们都要和角色绑定)。
角色类似于现实生活中的职位,现实生活中的角色。 它们都具有继承性,更高一级的职位会继承低级职位的权限。
将继承引入角色, 会减少角色的数量,增加角色的可维护性, 进而增加用户和资源的可维护性。
@startuml
note "RBAC(Role-Based Access Control) 基于角色的访问控制\nRBAC1对RBAC0进行了扩展,是RBAC的角色分层模型,\nRBAC1引入了角色继承概念(上下级包含关系)" as N1
User "0..*" - "0..*" Role
User -- Session
Role -- Session
Role "0..*" -o "1" Role
Role "0..*" -up- "0..*" Permission
Permission - Object
Permission - Operation
class Permission {
- ops : Operation
- ops : Object
}
@enduml
2.3 RBAC2
RBAC2,基于RBAC0模型的基础上,进行了角色的访问控制。
RBAC2模型中添加了责任分离关系。RBAC2的约束规定了权限被赋予角色时,或角色被赋予用户时,以及当用户在某一时刻激活一个角色时所应遵循的强制性规则。责任分离包括静态责任分离和动态责任分离。约束与用户-角色-权限关系一起决定了RBAC2模型中用户的访问许可,此约束有多种。
- *互斥角色 * :同一用户只能分配到一组互斥角色集合中至多一个角色,支持责任分离的原则。互斥角色是指各自权限互相制约的两个角色。对于这类角色一个用户在某一次活动中只能被分配其中的一个角色,不能同时获得两个角色的使用权。常举的例子:在审计活动中,一个角色不能同时被指派给会计角色和审计员角色。
- *基数约束 * :一个角色被分配的用户数量受限;一个用户可拥有的角色数目受限;同样一个角色对应的访问权限数目也应受限,以控制高级权限在系统中的分配。例如公司的领导人有限的;
- *先决条件角色 * :可以分配角色给用户仅当该用户已经是另一角色的成员;对应的可以分配访问权限给角色,仅当该角色已经拥有另一种访问权限。指要想获得较高的权限,要首先拥有低一级的权限。就像我们生活中,校长是从副校长中选举的一样。
- *运行时互斥 * :例如,允许一个用户具有两个角色的成员资格,但在运行中不可同时激活这两个角色。
@startuml
note "RBAC(Role-Based Access Control) 基于角色的访问控制\nRBAC2是基于RBAC0的扩展,主要引入了SSD(静态职责分离)和DSD(动态职责分离)\n\nSSD主要应用在用户和角色之间(授权阶段),主要约束:\n1、互斥角色:同一个用户不能授予互斥类的角色\n2、基数约束:一个用户所拥有的角色是有限的,一个角色所拥有的许可是有限的\n3、先决条件角色:用户想得到的高级权限,必须先拥有低级权限\n\nDSD是会话和角色之间的约束,主要动态决定怎样计划角色" as N1
note "静态职责分离\n SSD" as ssd
ssd -- User
ssd -- Role
User "0..*" - "0..*" Role
User -- Session
Role -- Session
note "动态态职责分离\n DSD" as dsd
Session - dsd
Role -- dsd
Role "0..*" - "0..*" Permission
Permission - Object
Permission - Operation
class Permission {
- ops : Operation
- ops : Object
}
@enduml
SSD:给用户绑定角色增加约束,DSD:约束某一个具体会话的角色,使其失效。
SSD的好处:给用户绑定角色增加约束,可以限制用户对某些角色的获取。
DSD的好处:角色除了能管理用户的权限, 还能管理这个用户在当前会话的权限,使得权限管理的粒度到会话级别。
2.4 RBAC3
RBAC3,也就是最全面级的权限管理,它是基于RBAC0的基础上,将RBAC1和RBAC2进行整合,是最全面的,也是最复杂的。
@startuml
note "RBAC(Role-Based Access Control) 基于角色的访问控制\nRBAC3 = RBAC1 + RBAC2" as N1
note "静态职责分离\n SSD" as ssd
ssd -- User
ssd -- Role
User "0..*" - "0..*" Role
User -- Session
Role -- Session
Role "0..*" -o "1" Role
Role "0..*" -up- "0..*" Permission
note "动态态职责分离\n DSD" as dsd
Session - dsd
Role -- dsd
Permission - Object
Permission - Operation
class Permission {
- ops : Operation
- ops : Object
}
@enduml
3.RBAC优缺点
RBAC模型没有提供操作顺序控制机制。这一缺陷使得RBAC模型很难应用关于那些要求有严格操作次序的实体系统。
例如,在购物控制系统中要求系统对购买步骤的控制,在客户未付款之前不应让他把商品拿走。RBAC模型要求把这种控制机制放到模型
RBAC的优点是他将操作和对象作为授权的对象,他们是授权的最小粒度,在此基础上引入角色的概念,使得用户和资源(操作)解耦,此时角色变成了双方的关联,引入角色的继承机制,便于维护角色这个中间关联。SSD,增加了用户获取角色的约束。DSD, 使得权限的授权对象由用户到会话,粒度更细。
由于RBAC最初将资源(操作和对象)作为授权的最小粒度,将业务的流程顺序和资源解耦,专注于资源的鉴权,导致没有了业务流程的鉴权。
有两种解决方案,一种是将业务流程的鉴权交由业务处理,由业务代码判断、解决,另一种是基于RBAC,增加业务流程的鉴权。
主流的方案是方案一,方案二有点类似工作流,本质还是把业务鉴权当做业务的一部分。
4.RBAC模型数据库建模
RBAC(Role-Based Access Control,基于角色的访问控制),就是用户通过角色与权限进行关联。简单地说,一个用户拥有若干角色,每一个角色拥有若干权限。这样,就构造成“用户-角色-权限”的授权模型。在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系。(如下图)
@startuml entity 用户 { *用户id : number <<generated>> -- *用户名 : text ... } entity 角色 { *角色id : number <<generated>> -- *角色名 : text ... } entity 权限 { *权限id : number <<generated>> -- *权限标识 : text ... } entity 用户角色关联 { *用户id : number <<FK>> *角色id : number <<FK>> } entity 角色权限关联 { *角色id : number <<FK>> *权限id : number <<FK>> } 用户 ||--o{ 用户角色关联 角色 ||--o{ 用户角色关联 角色 ||--o{ 角色权限关联 权限 ||--o{ 角色权限关联 @enduml
这是RBAC0的数据模型, 将对象和操作统一为了权限(对资源的权限)。
用户与角色、角色与权限的多对多关系使用关联表保存。
角色是什么?可以理解为一定数量的权限的集合,权限的载体。例如:一个论坛系统,“超级管理员”、“版主”都是角色。版主可管理版内的帖子、可管理版内的用户等,这些是权限。要给某个用户授予这些权限,不需要直接将权限授予用户,可将“版主”这个角色赋予该用户。 当用户的数量非常大时,要给系统每个用户逐一授权(授角色),是件非常烦琐的事情。这时,就需要给用户分组,每个用户组内有多个用户。除了可给用户授权外,还可以给用户组授权。这样一来,用户拥有的所有权限,就是用户个人拥有的权限与该用户所在用户组拥有的权限之和。(下图为用户组、用户与角色三者的关联关系)
@startuml entity 用户组 { *用户组id : number <<generated>> -- *用户组名称 : text *父用户组id : number <<FK>> ... } entity 用户组与用户关联 { *用户组id : number <<generated>> *用户ID : number <<FK>> ... } entity 用户组角色关联 { *用户组id : number <<generated>> *角色ID : number <<FK>> ... } entity 角色 { *角色id : number <<generated>> -- *角色名 : text ... } entity 用户 { *用户id : number <<generated>> -- *用户名 : text ... } entity 用户角色关联 { *用户id : number <<FK>> *角色id : number <<FK>> } 用户组 ||--o{ 用户组与用户关联 用户组 ||--o{ 用户组角色关联 用户组与用户关联 }o--|| 用户 用户组角色关联 }o--|| 角色 用户 }o--|| 用户角色关联 角色 }o--|| 用户角色关联 @enduml
RBAC0使用角色来聚集权限, 用户绑定角色。他解决了权限的管理问题,此时对角色友好,对用户不友好,每次创建一个用户,都需给该用户绑定很多角色,维护用户与角色的关联不友好。
其实RBAC1中的角色的继承可以解决这个问题,通过角色的继承机制减少用户与角色关联的复杂性。
之所以引入用户组,是因为RBAC1虽然可以解决用户与角色绑定的复杂性, 但是他的代价是增加了角色的维护难度, 权限管理者要维护角色之间的继承关系, 进而便于维护用户与角色的绑定。 这种方式增加了用户、角色、权限三者的耦合度。
由于此时角色的继承性需要考虑用户之间的关系,当出现多个用户的权限关系非常负责, 角色的继承无法全部满足时,要么这一部分全部都不使用继承,要么只满足一部分用户的继承关系,随着时间的推移,RBAC1会越来越难以维护,或者慢慢退化到RBAC0中。
此时用户组的概念就出现了, 每个用户组之间是相互独立的(每个用户组都可以绑定自己的多个角色),此时,简化用户与角色的授权就可以不耦合角色系统,角色系统可以很纯粹的只处理角色与权限的关联。用户与角色关联的简化就依赖用户组了。
当然在业务上,用户组往往是有业务上的继承性,类似树状关系(一对多),增加了父用户组id,扩展了这个功能。
用户组概念的引入是上述原因,RBAC1中引入角色的继承,是为了简化角色的继承,或者说是通用角色的继承性,不论任何用户,或者说不考虑任何用户, 这两个角色都具有天然的继承性,使用角色的继承可以增加角色的可维护性,而不会和用户耦合。如果不使用角色的继承性,角色的数量将会增多,角色的维护难度将会增大,角色与用户的绑定关系将会增多,此时,才可以使用RBAC1中角色的继承。
以上便是用户组与角色继承的区别。
在应用系统中,权限表现成什么?对功能模块的操作,对上传文件的删改,菜单的访问,甚至页面上某个按钮、某个图片的可见性控制,都可属于权限的范畴。有些权限设计,会把功能操作作为一类,而把文件、菜单、页面元素等作为另一类,这样构成“用户-角色-权限-资源”的授权模型。而在做数据表建模时,可把功能操作和资源统一管理,也就是都直接与权限表进行关联,这样可能更具便捷性和易扩展性。(见下图)
@startuml entity 菜单 { *菜单id : number <<generated>> -- *菜单名称 : text *菜单URL : text *父菜单id : number <<FK>> } entity 页面元素 { *页面元素id : number <<generated>> -- *页面元素编码 : text } entity 文件 { *文件id : number <<generated>> -- *文件名 : text *文件路径 : text } entity 权限菜单关联 { *权限id : number <<FK>> *菜单id : number <<FK>> } entity 权限页面元素关联 { *权限id : number <<FK>> *页面元素id : number <<FK>> } entity 权限文件关联 { *权限id : number <<FK>> *文件id : number <<FK>> } entity 权限 { *权限id : number <<generated>> -- *权限类型 : text } entity 功能操作 { *操作id : number <<generated>> -- *操作名称 : text *操作编码 : text *拦截URL前缀 : text *父操作id : number <<FK>> } entity 权限操作关联 { *权限id : number <<FK>> *操作id : number <<FK>> } 菜单 ||--o{ 权限菜单关联 页面元素 ||--o{ 权限页面元素关联 文件 ||--o{ 权限文件关联 权限菜单关联 }o--|| 权限 权限页面元素关联 }o--|| 权限 权限文件关联 }o--|| 权限 功能操作 ||--o{ 权限操作关联 权限 ||--o{ 权限操作关联 @enduml
这是对资源的一种细化。
将对象与操作细化为菜单、页面元素、文件和功能(操作)。
在有的系统中, 会将这些资源的获取统一为接口调用,那所有的资源都变成了接口。
菜单资源就是前端页面(前端路由),功能操作就是接口, 文件变成了获取文件地址接口(或者作为其他接口的某一个属性),页面元素变成了获取页面配置的接口。将所有资源统一为接口,资源即接口。权限控制接口,也就控制了所有资源。
请留意权限表中有一列“权限类型”,我们根据它的取值来区分是哪一类权限,如“MENU”表示菜单的访问权限、“OPERATION”表示功能模块的操作权限、“FILE”表示文件的修改权限、“ELEMENT”表示页面元素的可见性控制等。 这样设计的好处有二。其一,不需要区分哪些是权限操作,哪些是资源,(实际上,有时候也不好区分,如菜单,把它理解为资源呢还是功能模块权限呢?)。其二,方便扩展,当系统要对新的东西进行权限控制时,我只需要建立一个新的关联表“权限XX关联表”,并确定这类权限的权限类型字符串。 这里要注意的是,权限表与权限菜单关联表、权限菜单关联表与菜单表都是一对一的关系。(文件、页面权限点、功能操作等同理)。也就是每添加一个菜单,就得同时往这三个表中各插入一条记录。这样,可以不需要权限菜单关联表,让权限表与菜单表直接关联,此时,须在权限表中新增一列用来保存菜单的ID,权限表通过“权限类型”和这个ID来区分是种类型下的哪条记录。
到这里,RBAC权限模型的扩展模型的完整设计图如下:
@startuml entity 用户组 { *用户组id : number <<generated>> -- *用户组名称 : text *父用户组id : number <<FK>> ... } entity 用户组与用户关联 { *用户组id : number <<generated>> *用户ID : number <<FK>> ... } entity 用户组角色关联 { *用户组id : number <<generated>> *角色ID : number <<FK>> ... } entity 角色 { *角色id : number <<generated>> -- *角色名 : text ... } entity 用户 { *用户id : number <<generated>> -- *用户名 : text ... } entity 用户角色关联 { *用户id : number <<FK>> *角色id : number <<FK>> } 用户组 ||--o{ 用户组与用户关联 用户组 ||--o{ 用户组角色关联 用户组与用户关联 }o--|| 用户 用户组角色关联 }o--|| 角色 用户 }o--|| 用户角色关联 角色 }o--|| 用户角色关联 entity 权限 { *权限id : number <<generated>> -- *权限标识 : text ... } entity 角色权限关联 { *角色id : number <<FK>> *权限id : number <<FK>> } 角色 ||--o{ 角色权限关联 权限 ||--o{ 角色权限关联 entity 菜单 { *菜单id : number <<generated>> -- *菜单名称 : text *菜单URL : text *父菜单id : number <<FK>> } entity 页面元素 { *页面元素id : number <<generated>> -- *页面元素编码 : text } entity 文件 { *文件id : number <<generated>> -- *文件名 : text *文件路径 : text } entity 权限菜单关联 { *权限id : number <<FK>> *菜单id : number <<FK>> } entity 权限页面元素关联 { *权限id : number <<FK>> *页面元素id : number <<FK>> } entity 权限文件关联 { *权限id : number <<FK>> *文件id : number <<FK>> } entity 功能操作 { *操作id : number <<generated>> -- *操作名称 : text *操作编码 : text *拦截URL前缀 : text *父操作id : number <<FK>> } entity 权限操作关联 { *权限id : number <<FK>> *操作id : number <<FK>> } 菜单 ||--o{ 权限菜单关联 页面元素 ||--o{ 权限页面元素关联 文件 ||--o{ 权限文件关联 权限菜单关联 }o--|| 权限 权限页面元素关联 }o--|| 权限 权限文件关联 }o--|| 权限 权限 ||--o{ 权限操作关联 功能操作 ||--o{ 权限操作关联 @enduml
随着系统的日益庞大,为了方便管理,可引入角色组对角色进行分类管理,跟用户组不同,角色组不参与授权。例如:某电网系统的权限管理模块中,角色就是挂在区局下,而区局在这里可当作角色组,它不参于权限分配。另外,为方便上面各主表自身的管理与查找,可采用树型结构,如菜单树、功能树等,当然这些可不需要参于权限分配。
如之前所述, 角色组是为了便于维护角色与资源的绑定, 所以角色组与用户无直接关联,用户组是为了便于维护用户与角色的绑定。
菜单树、功能树是为了便于维护资源。
辨析:角色与用户组有何区别?
两者的主要差别是:用户组是用户的集合,但不是许可权的集合;而角色却同时具有用户集合和许可权集合的概念,角色的作用把这两个集合联系在一起的中间媒介。
在一个系统中,如果用户组的许可权和成员仅可以被系统安全员修改的话,在这种机制下,用户组的机制是非常接近于角色的概念的。角色也可以在用户组的基础上实现,这有利于保持原有系统中的控制关系。在这种情况下,角色相当于一个策略部件,与用户组的授权及责任关系相联系,而用户组是实现角色的机制,因此,两者之间是策略与实现机制之间的关系。
上述的使用用户组实现角色,这种方式会耦合用户、角色、权限三者,一般不建议。
3. ACL模型
访问控制列表,是前几年盛行的一种权限设计,它的核心在于用户直接和权限挂钩。
RBAC的核心是用户只和角色关联,而角色代表对了权限,这样设计的优势在于使得对用户而言,只需角色即可以,而某角色可以拥有各种各样的权限并可继承。
ACL和RBAC相比缺点在于由于用户和权限直接挂钩,导致在授予时的复杂性,虽然可以利用组来简化这个复杂性,但仍然会导致系统不好理解,而且在取出判断用户是否有该权限时比较的困难,一定程度上影响了效率。
ACL没有角色的概念,用户与资源直接耦合。