转:权限系统设计模型分析(DAC,MAC,RBAC,ABAC)

 

 
 

术语

这里对后面会用到的词汇作一个说明,老司机请直接翻到常见设计模式。php

用户

发起操做的主体。html

对象(Subject)

指操做所针对的客体对象,好比订单数据或图片文件。mongodb

权限控制表 (ACL: Access Control List)

用来描述权限规则或用户和权限之间关系的数据表。数据库

权限 (Permission)

用来指代对某种对象的某一种操做,例如“添加文章的操做”。apache

权限标识

权限的代号,例如用“ARTICLE_ADD”来指代“添加文章的操做”权限。centos

常见设计模式

自主访问控制(DAC: Discretionary Access Control)

系统会识别用户,而后根据被操做对象(Subject)的权限控制列表(ACL: Access Control List)或者权限控制矩阵(ACL: Access Control Matrix)的信息来决定用户的是否能对其进行哪些操做,例如读取或修改。设计模式

而拥有对象权限的用户,又能够将该对象的权限分配给其余用户,因此称之为“自主(Discretionary)”控制。安全

这种设计最多见的应用就是文件系统的权限设计,如微软的NTFS。架构

DAC最大缺陷就是对权限控制比较分散,不便于管理,好比没法简单地将一组文件设置统一的权限开放给指定的一群用户。app

 
Windows的文件权限

强制访问控制(MAC: Mandatory Access Control)

MAC是为了弥补DAC权限控制过于分散的问题而诞生的。在MAC的设计中,每个对象都都有一些权限标识,每一个用户一样也会有一些权限标识,而用户可否对该对象进行操做取决于双方的权限标识的关系,这个限制判断一般是由系统硬性限制的。好比在影视做品中咱们常常能看到特工在查询机密文件时,屏幕提示须要“没法访问,须要一级安全许可”,这个例子中,文件上就有“一级安全许可”的权限标识,而用户并不具备。

MAC很是适合机密机构或者其余等级观念强烈的行业,但对于相似商业服务系统,则由于不够灵活而不能适用。

 
RedHat MLS

Red Hat: MLS

基于角色的访问控制(RBAC: Role-Based Access Control)

由于DAC和MAC的诸多限制,因而诞生了RBAC,而且成为了迄今为止最为普及的权限设计模型。

RBAC在用户和权限之间引入了“角色(Role)”的概念(暂时忽略Session这个概念):

 
RBAC核心设计

图片来自Apache Directory

如图所示,每一个用户关联一个或多个角色,每一个角色关联一个或多个权限,从而能够实现了很是灵活的权限管理。角色能够根据实际业务需求灵活建立,这样就省去了每新增一个用户就要关联一遍全部权限的麻烦。简单来讲RBAC就是:用户关联角色,角色关联权限。另外,RBAC是能够模拟出DAC和MAC的效果的。

例如数据库软件MongoDB即是采用RBAC模型,对数据库的操做都划分红了权限(MongoDB权限文档):

权限标识 说明
find 具备此权限的用户能够运行全部和查询有关的命令,如:aggregate、checkShardingIndex、count等。
insert 具备此权限的用户能够运行全部和新建数据有关的命令:insert和create等。
collStats 具备此权限的用户能够对指定database或collection执行collStats命令。
viewRole 具备此权限的用户能够查看指定database的角色信息。
 

基于这些权限,MongoDB提供了一些预约义的角色(MongoDB预约义角色文档,用户也能够本身定义角色):

角色 find insert collStats viewRole
read    
readWrite  
dbAdmin    
userAdmin      

最后授予用户不一样的角色,就能够实现不一样粒度的权限分配了。

目前市面上绝大部分系统在设计权限系统时都采用RBAC模型。然而也有的系统错误地实现了RBAC,他们采用的是判断用户是否具备某个角色而不是判断权限,例如如下代码:

<?php if ($user->hasRole('hr')) { // 执行某种只有“HR”角色才能作的功能,例如给员工涨薪… // ... } 

若是后期公司规定部门经理也能够给员工涨薪,这时就不得不修改代码了。

以上基本就是RBAC的核心设计(RBAC Core)。而基于核心概念之上,RBAC规范还提供了扩展模式。

角色继承(Hierarchical Role)

 
RBAC 1

带有角色继承的RBAC。图片来自Apache Directory

顾名思义,角色继承就是指角色能够继承于其余角色,在拥有其余角色权限的同时,本身还能够关联额外的权限。这种设计能够给角色分组和分层,必定程度简化了权限管理工做。

职责分离(Separation of Duty)

为了不用户拥有过多权限而产生利益冲突,例如一个篮球运动员同时拥有裁判的权限(看一眼就给你判犯规狠不狠?),另外一种职责分离扩展版的RBAC被提出。

职责分离有两种模式:

  • 静态职责分离(Static Separation of Duty):用户没法同时被赋予有冲突的角色。
  • 动态职责分离(Dynamic Separation of Duty):用户在一次会话(Session)中不能同时激活自身所拥有的、互相有冲突的角色,只能选择其一。
 
RBAC 2

静态职责分离。图片来自Apache Directory

 
RBAC 3

动态职责分离。图片来自Apache Directory

讲了这么多RBAC,都还只是在用户和权限之间进行设计,并无涉及到用户和对象之间的权限判断,而在实际业务系统中限制用户可以使用的对象是很常见的需求。例如华中区域的销售没有权限查询华南区域的客户数据,虽然他们都具备销售的角色,而销售的角色拥有查询客户信息的权限。

那么咱们应该怎么办呢?

用户和对象的权限控制

在RBAC标准中并无涉及到这个内容(RBAC基本只能作到对一类对象的控制),可是这里讲几种基于RBAC的实现方式。

首先咱们看看PHP框架Yii 1.X的解决方案(2.X中代码更为优雅,但1.X的示例代码更容易看明白):

<?php $auth=Yii::app()->authManager; $auth->createOperation('createPost','create a post'); $auth->createOperation('readPost','read a post'); $auth->createOperation('updatePost','update a post'); $auth->createOperation('deletePost','delete a post'); // 主要看这里。 // 这里建立了一个名为`updateOwnPost`的权限,而且写了一段代码用来检验用户是否为该帖子的做者 $bizRule='return Yii::app()->user->id==$params["post"]->authID;'; $task=$auth->createTask('updateOwnPost','update a post by author himself',$bizRule); $task->addChild('updatePost'); $role=$auth->createRole('reader'); $role->addChild('readPost'); $role=$auth->createRole('author'); $role->addChild('reader'); $role->addChild('createPost'); $role->addChild('updateOwnPost'); $role=$auth->createRole('editor'); $role->addChild('reader'); $role->addChild('updatePost'); $role=$auth->createRole('admin'); $role->addChild('editor'); $role->addChild('author'); $role->addChild('deletePost'); 

实现效果:

 
Yii 1.X权限图

图片来自Yii官方WiKi

在这个Yii的官方例子中,updateOwnPost在判断用户是否具备updatePost权限的基础上更进一步判断了用户是否有权限操做这个特定的对象,而且这个判断逻辑是经过代码设置的,很是灵活。

不过大部分时候咱们并不须要这样的灵活程度,会带来额外的开发和维护成本,而另外一种基于模式匹配规则的对象权限控制可能更适合。例如判断用户是否对Id为123的文章具备编辑的权限,代码多是这样的:

<?php // 假设articleId是动态获取的 $articleId = 123; if ($user->can("article:edit:{$articleId}")) { // ... } 

而给用户受权则有多种方式能够选择:

<?php // 容许用户编辑Id为123的文章 $user->grant('article:edit:123'); // 使用通配符,容许用户编辑全部文章 $user->grant('article:edit:*'); 

虽然不及Yii方案的灵活,但某些场景下这样就够用了。

若是你们还有更好的方案,欢迎在评论中提出。

基于属性的权限验证(ABAC: Attribute-Based Access Control)

ABAC被一些人称为是权限系统设计的将来。

不一样于常见的将用户经过某种方式关联到权限的方式,ABAC则是经过动态计算一个或一组属性来是否知足某种条件来进行受权判断(能够编写简单的逻辑)。属性一般来讲分为四类:用户属性(如用户年龄),环境属性(如当前时间),操做属性(如读取)和对象属性(如一篇文章,又称资源属性),因此理论上可以实现很是灵活的权限控制,几乎能知足全部类型的需求。

例如规则:“容许全部班主任在上课时间自由进出校门”这条规则,其中,“班主任”是用户的角色属性,“上课时间”是环境属性,“进出”是操做属性,而“校门”就是对象属性了。为了实现便捷的规则设置和规则判断执行,ABAC一般有配置文件(XML、YAML等)或DSL配合规则解析引擎使用。XACML(eXtensible Access Control Markup Language)是ABAC的一个实现,可是该设计过于复杂,我尚未彻底理解,故不作介绍。

总结一下,ABAC有以下特色:

  1. 集中化管理
  2. 能够按需实现不一样颗粒度的权限控制
  3. 不须要预约义判断逻辑,减轻了权限系统的维护成本,特别是在需求常常变化的系统中
  4. 定义权限时,不能直观看出用户和对象间的关系
  5. 规则若是稍微复杂一点,或者设计混乱,会给管理者维护和追查带来麻烦
  6. 权限判断须要实时执行,规则过多会致使性能问题

既然ABAC这么好,那最流行的为何仍是RBAC呢?

我认为主要仍是由于大部分系统对权限控制并无过多的需求,并且ABAC的管理相对来讲太复杂了。Kubernetes便由于ABAC太难用,在1.8版本里引入了RBAC的方案

ABAC有时也被称为PBAC(Policy-Based Access Control)或CBAC(Claims-Based Access Control)。

结语

权限系统设计可谓博大精深,这篇文章只是介绍了一点皮毛。

随着人类在信息化道路上越走越远,权限系统的设计也在不断创新,但目前好像处在了平台期。

可能由于在RBAC到ABAC之间有着巨大的鸿沟,没法轻易跨越,也多是一些基于RBAC的微创新方案还不够规范化从而作到普及。不过在服务化架构的浪潮下,将来这一块必然有极高的需求,也许巨头们已经开始布局了

相关文章
相关标签/搜索