传统的 RBAC 指的是基于角色的访问控制(Role-Based Access Control
),在安全领域中是被广泛使用的一种访问控制机制。应用程序因为提供的一个或一组功能的目标用户不同而通过角色对用户进行区分,每个角色只允许操作被授权了的一组功能。
新的 RBAC (The New RBAC)指的是基于资源的访问控制(Resource-Based Access Control
),相比传统 RBAC,新 RBAC 有着诸多优点。
传统意义上的 RBAC(基于角色的访问控制)
角色是一个抽象概念,通常代表一组对资源的操作能力,角色被分配给用户,因此用户就可以 “做” 角色能够做的事情。例如一个用户登陆到应用程序,用户拥有 “帖子管理员” 的角色,那么用户就可以做帖子管理员能够做的事情,比如删除帖子,发表帖子等。
由于角色是一个抽象概念,需要一个具象的表示,我们才能在开发过程中实现相关的控制逻辑,再拿 “帖子管理员” 为例,代码中我们往往会用字符串来表示角色,如 PostManager
,那么在开发时可能就会出现如下的代码段:
if (user.hasRole("PostManager")) {
// 发帖,删帖或编辑帖子等操作
} else {
// 没有权限,返回错误或提示...
}
开发者的脑海中清晰的知道 PostManager
能够进行发帖,删帖等操作,但从代码中我们能看到的是:拥有 PostManager
角色的用户能够执行某些操作,而 “这些” 操作,具体是什么,却没有一个明确的定义。这是一种 隐式的权限控制。
假设有这样的一个需求场景,系统中加入了一个新的角色:ProjectManager
,它同样拥有管理帖子的能力,那么为了响应这个新需求,就需要对上面的代码进行修改。
if (user.hasRole("PostManager") || user.hasRole("ProjectManager")) {
// 发帖,删帖或编辑帖子等操作
} else {
// 没有权限,返回错误或提示...
}
代码修改成本似乎很小,但如果是一个上线项目,开发者需要同时增加相应的单元测试,完成后续 QA 流程,然后再安排发布这个修改。过了不久,新需求又说:“移除 ProjectManager
的帖子管理权限”,或者 “ DepartmentManager
也需要管理帖子” ...
理想情况下应该只有系统数据模型有修改时才进行代码修改,如果把角色也看成是数据的一种,新增或删除角色不应该导致代码修改。
新的 RBAC(基于资源的访问控制)
从上面的介绍我们可以知道,基于角色的访问控制是隐式的,安全策略变化时,代码会被迫进行修改。如果能在代码运行时安全的修改安全策略而不需要通过代码发布来使之生效就太好了。
从根本上来说,访问控制是为了控制用户是否能对某种资源执行某种操作,从而达到保护资源的目的。我们可以参照角色的表示方法用字符串对 “具体资源的某种操作” 进行定义,如 “编辑帖子” 的定义为:"post:edit"
,那么我们就可以写出如下代码:
if (user.isPermitted("post:edit")) {
// 编辑帖子
} else {
// 没有权限,返回错误或提示...
}
上面这种方式就是基于资源的访问控制,相比基于角色的访问控制,基于资源的访问控制有着更小的控制粒度,把关注点放在了最根本的 “用户是否具有对资源执行某种操作的能力” 上,这是一种显示的权限控制。
基于资源的访问控制带来的好处
减少代码重构,将访问控制的关注点放在更细粒度的资源上,因为资源的变化频率比起角色要低得多,开发者在开发某一功能时可以直接检查用户是否具有对资源执行操作的能力,而不是去检查用户的角色。
代码容易阅读,权限检查与代码当前执行的逻辑是对应的,而不像角色检查那样检查一个与当前操作间接相关的事物。
灵活的安全模型,上面的代码示例并没有对用户、组或角色进行规定, 这意味着可以支持任何安全模型设计,比如,权限可以直接分配给用户,也可以将它们分配给一个角色,再由角色分配给用户, 也许还有组的概念,组与角色有关联,等等。 这些可能性是开放的,可以根据应用进行定制,而不管怎么对权限进行组织,最终代码里检查权限的地方都是相同的,只需要检查具体权限,无需关心上层概念。
在运行时进行更改,由于权限检查代码直接与当前代码执行的操作相关,我们可以在应用程序运行时更改那些以权限为基础的上层安全模型中的关联关系,而不需要重构代码就可以使新的安全策略生效。