基于角色的访问控制
角色定义
[role_definition]
是RBAC角色继承关系的定义。 Casbin 支持 RBAC 系统的多个实例, 例如, 用户可以具有角色及其继承关系, 资源也可以具有角色及其继承关系。 这两个 RBAC 系统不会互相干扰。
此部分是可选的。 如果在模型中不使用 RBAC 角色, 则省略此部分。
[role_definition]
g = _, _
g2 = _, _
上述角色定义表明, g
是一个 RBAC系统, g2
是另一个 RBAC 系统。 _, _
表示角色继承关系的前项和后项,即前项继承后项角色的权限。 一般来讲,如果您需要进行角色和用户的绑定,直接使用g
即可。 当您需要表示角色(或者组)与用户和资源的绑定关系时,可以使用g
和 g2
这样的表现形式。 请参见 rbac_model 和 rbac_model_with_resource_roles 的示例。
在Casbin里,我们以policy表示中实际的用户角色映射关系 (或是资源-角色映射关系),例如:
p, data2_admin, data2, read
g, alice, data2_admin
这意味着 alice
是角色 data2_admin
的一个成员。 alice
在这里可以是用户、资源或角色。 Cabin 只是将其识别为一个字符串。
接下来在matcher中,应该像下面的例子一样检查角色信息:
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act
这意味着在请求中应该在policy中包含sub
角色。
note
- Casbin 只存储用户角色的映射关系。
- Cabin 没有验证用户是否是有效的用户,或者角色是一个有效的角色。 这应该通过认证来解决。
- RBAC 系统中的用户名称和角色名称不应相同。因为Casbin将用户名和角色识别为字符串, 所以当前语境下Casbin无法得出这个字面量到底指代用户
alice
还是角色alice
。 这时,使用明确的role_alice
,问题便可迎刃而解。 - 假设
A
具有角色B
,B
具有角色C
,并且A
有角色C
。 这种传递性在当前版本会造成死循环。
角色层次
Casbin 的 RBAC 支持 RBAC1 的角色层次结构功能,如果 alice
具有role1
, role1
具有role2
,则 alice
也将拥有 role2
并继承其权限。
下面是一个称为层次结构级别的概念。 因此, 此示例的层次结构级别为2。 对于Casbin中的内置角色管理器, 可以指定最大层次结构级别。 默认值为10。 这意味着终端用户 alice
只能继承10个级别的角色。
// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
rm := RoleManager{}
rm.allRoles = &sync.Map{}
rm.maxHierarchyLevel = maxHierarchyLevel
rm.hasPattern = false
return &rm
}
如何区分用户和角色?
在RBAC中,Casbin不对用户和角色进行区分。 它们都被视为字符串。 如果你只使用单层的RBAC模型(角色不会成为另一个角色的成员)。 可以使用 e.GetAllSubjects()
获取所有用户,e.GetAllRoles()
获取所有角色。 它们会为规则 g, u, r
分别列出所有的 u
和 r
。
但如果你在使用多层RBAC(带有角色继承),并且你的应用没有记录下一个名字(字符串)对应的是用户还是角色,或者你将用户和角色用相同的名字命名。 那么你可以可以给角色加上像 role::admin
的前缀再传递到Casbin中。 由此可以通过查看前缀来区分用户和角色。
如何查询隐性角色或权限?
当用户通过RBAC层次结构继承角色或权限,而不是直接在策略规则中分配它们时,我们将这种类型的分配称为 implicit
。 要查询这种隐式关系,需要使用以下两个api: GetImplicitRolesForUser()
以及 GetImplicitPermissionsForUser()
替代GetRolesForUser()
以及 GetPermissionsForUser()
. 有关详情,请参阅 this GitHub issue。
在 RBAC 中使用模式匹配
有时,您希望一些具有特定模式的subjects, object 或者 domains/tenants能够被自动授予角色。 RBAC中的模式匹配函数可以帮助做到这一点。 模式匹配函数与前一个函数共享相同的参数和返回值:matcher function。
模式匹配函数支持g的每一个参数
我们知道,在matcher里面RBAC通常被表示为 g(r.sub, p.sub)
接下来我们将使用如下策略:
p, alice, book_group, read
g, /book/1, book_group
g, /book/2, book_group
因此alice
可以阅读所有书籍,包括book 1
和book 2
。 但是当有数千本书时,如果我们仅仅使用g
策略规则将每本书一个一个地添加到书籍角色(或组),那将会是非常繁琐的。
不过,凭借着模式匹配函数,你可以把整个策略只用一行写下!
g, /book/:id, book_group
Casbin会自动将/book/1
和/book/2
匹配为模式/book/:id
。 您需要做的仅仅是向enforcer注册该方法,例如像这样:
e.AddNamedDomainMatchingFunc("g","KeyMatch2",util.KeyMatch2)
When Using a pattern matching function in domains/tenants, You need to register the function to enforcer and model. You also need to rebuild by calling e.BuildRoleLinks()
register keyMatch2
to enforcer and build role links again:
e.AddNamedDomainMatchingFunc("g","KeyMatch2",util.KeyMatch2)
e.BuildRoleLinks()
将 keyMatch2
向model进行注册:
m = g(r.sub, p.sub, r.dom) && keyMatch2(r.dom, p.dom) && r.obj == p.obj && r.act == p.act
如果你不懂 g(r.sub, p.sub, r.dom)
是什么意思, 请阅读 这部分. 简而言之, g(r.sub, p.sub, r.dom)
将检查用户 r.sub
在域内 r.dom
是否具有角色 p.sub
因此,这正是匹配器的工作方式。 您可以在 这里 查看完整的示例
除了上面的模式匹配语法外,我们还可以使用纯域模式。
例如,如果我们想要, sub
在这两个不同的域拥有访问权限, domain1
以及 domain2
,我们可以使用纯域模式匹配:
p, admin, domain1, data1, read
p, admin, domain1, data1, write
p, admin, domain2, data2, read
p, admin, domain2, data2, write
g, alice, admin, *
g, bob, admin, domain2
在这个示例中,我们想要 alice
可以读写 data
在域1和域2中,模式匹配 *
在 g
使得 alice
拥有两个域的权限
通过使用模式匹配,特别是在一些比较复杂的环境中有很多的域以及我们需要考虑很多物体的时候,我们可以更简洁高效地实现 policy_definition
。