Casbin 是一个高效强大的开源访问控制框架,它支持 ACL、RBAC、ABAC 等多种模型。

Model,即模型,是 Casbin 的一个重要组成部分,模型的定义简单而复杂,且充满学问Model 的作用,是对权限模型的定义,例如:aclrbacabac 等权限模型。

模型配置主要有五个部分:[request_definition],[policy_definition],[role_definition],[policy_effect] 和 [matchers],分别表示请求定义、策略定义、角色定义、策略效果定义、匹配器,其中 [role_definition] 角色定义是用于基于角色的模型(RBAC),支持用 # 开头表示注释。

请求定义

[request_definition]
r = sub, obj, act

请求的定义,定义了决策器决策 enforce() 时传入的参数。sub, obj, act 就表示 enforce() 方法需传入 3 个参数,支持增加或删除参数数量,保证顺序和数量一一对应即可。

定义的参数名也可以修改,例如,要表示用户对接接口地址的权限,

则请求可以定义为:

[request_definition]
r = uid, uri, method

那么决策时可以这样使用:

enforce(888, '/api/post/add', 'POST');

意思是需要决策 uid 为 888 的用户对接口 /api/post/add 是否具有 POST 请求的权限。

策略定义

[policy_definition]
p = sub, obj, act

策略定义,它定义了策略中值的含义及顺序,参数名和数量同样支持修改。

假设,我们的策略表有如下的策略记录:

v0 v1 v2 v3 v4
p 886 /api/post/add POST 2025-01-10 08:16:01
p 887 /api/post/delete DELETE 2025-01-10 08:20:11
p 888 /api/post/list GET 2025-01-10 08:30:22

那么,我们的策略可以定义为:

[policy_definition]
p = uid, uri, method, created_at

这个四个参数就依次表示了策略中的 v1~v4 列,v0 可以理解为策略类型或分组,就是定义中的 p

Casbin 在加载策略的时候,就会按照这个对应关系加载,使用某条策略中的某个字段时可以用p.uid的方式。

角色定义

[role_definition]
g = _, _

角色定义,表明了用户和角色的继承关系,通常用于基于角色的权限模型。

其中 _, _,两个下划线,可以称作前向后项,表示前向继承后项,通常用于表示用户属于某个角色,或者角色继承另一个角色

如果为 _, _, _ 三个下划线,用于多租户模型,则最后一项表示多租户里的

策略效果定义

[policy_effect]
e = some(where (p.eft == allow))

策略的定义的取值是固定值,并且是系统内置的硬编码,不支持自定义。目前支持以下几种配置:

some(where (p.eft == allow)),表示在所有命中的策略中,只要有一条allow的策略,那么结果就是true,通俗来讲,就是一票赞成制

!some(where (p.eft == deny)),表示在命中的策略中,如果没有deny的,那最终的决策结果就是true,否则就是false,即一票否决制

some(where (p.eft == allow)) && !some(where (p.eft == deny)),表示在所有命中的策略中,要有一条allow的策略,并且不能有deny的策略,则最终决策结果就是true,例如:只要有人赞成且没有人反对即通过。

priority(p.eft) || deny,用于隐式/显式优先级模型,以命中的策略的eft的值作为决策结果,如果没有命中,或者eft没有明确的结果,那么就是false

subjectPriority(p.eft),用于基于用户和角色的层级关系的优先级模型。

匹配器

[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

匹配器,是对匹配规则的定义,m 的值是一个表达式。在调用enforce(...)方法进行决策执行的时候,会根据表达式中的变量带入运算执行表达式,依据表达式的执行结果来决定策略是否命中或未命中。

匹配器支持常用的算术运算符如 +, -, *, /,以及一些逻辑运算符如 &&, ||, !,并且还支持内置函数和自定义函数,像例子中的 g(r.sub, p.sub) 就是一个内置函数(g(...))。

这个表达式中的变量名就是前面请求定义、策略定义中的定义的名称,支持自行修改变量名,保证上下文一致即可。比如前文我们演示了uid, uri, method 这样的定义,那么表达式中可以这样使用r.uri == p.uri && r.method == p.method

关于表达式的解析运算的库,大多是第三方库,表达式的解析运算对性能有重要影响,可以参考官方文档。

最后

这篇文章主要介绍了 Casbin 中 Model 的语法,这些语法并不限定编程语言,同样适用于Java PHP Python Go等其他语言的 Casbin。

正如文章开头所说,可以看出 Casbin 的 Model 的语法及配置,简单而复杂,且充满学问。