Casbin 中 Model 的语法详解:从请求、策略到匹配器一次看懂

Noah
Noah
发布时间:2026-08-25| 86人浏览过
原创

前言

很多人第一次接触 Casbin 时,会觉得 Model 只是一个配置文件:字段不多,语法也不复杂。但真正把权限系统跑起来后,很快就会发现,访问控制能否说得清、改得稳、查得准,关键往往就卡在 Model 的定义上。本文按 Casbin Model 的五个部分逐段拆解,结合 enforce() 的入参、策略表字段映射、RBAC 继承和效果表达式来说明每段配置到底控制了什么,也帮助你判断不同写法分别适合什么场景。

用卡片和箭头展示 Casbin 请求定义与策略定义如何一一对应的暖色专栏配图
请求与策略字段如何对应先把请求参数和策略字段对齐,后续匹配表达式才有稳定语义。

很多人第一次接触 Casbin 时,会觉得 Model 只是一个配置文件:字段不多,语法也不复杂。但真正把权限系统跑起来后,很快就会发现,访问控制能否说得清、改得稳、查得准,关键往往就卡在 Model 的定义上。本文按 Casbin Model 的五个部分逐段拆解,结合 enforce() 的入参、策略表字段映射、RBAC 继承和效果表达式来说明每段配置到底控制了什么,也帮助你判断不同写法分别适合什么场景。

请求定义和策略定义:先把输入与字段对齐

Casbin 是一个高效强大的开源访问控制框架,支持 ACL、RBAC、ABAC 等多种模型。无论最终采用哪一种模型,最先要理清的都是两件事:请求里会传什么,策略里又按什么顺序存。

Model 本质上就是权限模型的定义,例如 acl、rbac、abac 等。它的配置主要由五个部分组成:[request_definition]、[policy_definition]、[role_definition]、[policy_effect] 和 [matchers]。其中 [role_definition] 用于基于角色的模型,另外配置里也支持用 # 开头写注释。

请求定义决定 enforce() 要接收什么

[request_definition]
r = sub, obj, act

请求定义描述的是:当你调用决策器的 enforce() 方法时,需要传入哪些参数。上面这段里,sub, obj, act 表示需要传入 3 个参数,通常可理解为主体、对象、动作。

这里最重要的约束不是名字,而是顺序和数量必须一一对应。你可以增减参数,也可以改名,只要调用时与定义保持一致即可。

例如,如果你的业务更接近接口权限,而不是传统的“用户-资源-操作”,可以直接改成下面这样:

[request_definition]
r = uid, uri, method

那么决策时就可以这样传入:

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

这表示:要判断 uid 为 888 的用户,是否拥有对接口 /api/post/add 发起 POST 请求的权限。

也就是说,请求定义不是装饰性命名,而是在明确告诉 Casbin:后续表达式里能用哪些请求变量,以及它们分别对应调用参数中的第几个值。

策略定义决定策略表每一列怎么解释

[policy_definition]
p = sub, obj, act

与请求定义类似,策略定义负责说明策略中每个值的含义和顺序。它同样支持修改参数名和数量。

用便签和裁决卡片展示 Casbin 多种 policy effect 结果差异的暖色专栏配图
多条策略命中后怎么裁决命中多条策略时,真正决定放行或拒绝的是策略效果定义。

假设策略表中有这样的记录:

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、p.uri、p.method 这样的写法。

很多模型配置看起来“没问题”,但实际匹配始终不生效,往往不是逻辑错了,而是请求定义和策略定义没有对齐:一边写的是 sub, obj, act,另一边业务理解的却是 uid, uri, method。名字不是硬性要求,但语义和顺序必须统一。

角色定义:RBAC 里的继承关系怎么表达

[role_definition]
g = _, _

角色定义用于描述用户与角色、角色与角色之间的继承关系,通常出现在基于角色的访问控制模型里,也就是 RBAC。

这里的 _, _ 可以理解为前项和后项,表示“前项继承后项”。在实际场景中,最常见的两种含义是:

  • 某个用户属于某个角色;
  • 某个角色继承另一个角色。

因此,g 这部分并不是在直接定义权限,而是在定义一条可被匹配器引用的关系链。后续当表达式里出现 g(r.sub, p.sub) 这种调用时,Casbin 就会沿着这里定义的继承关系去判断请求主体是否能匹配到策略主体。

如果角色定义写成 _, _, _ 三个下划线,则通常表示多租户模型,多出来的最后一项就是租户里的“域”。这类写法适用于同一个用户或角色名称会在不同租户中重复出现,但权限边界必须隔离的场景。

换句话说,角色定义的重点不在语法本身,而在你是否要把权限判断建立在“直接命中”之外的继承关系上。一旦进入 RBAC 或多租户 RBAC,g 的定义就会直接影响最终是否能匹配到策略。

策略效果定义:命中多条策略后,结果怎么算

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

当请求可能命中多条策略时,最终返回 true 还是 false,就由策略效果定义决定。这里的可选写法不是任意表达式,而是 Casbin 内置的固定值,属于系统硬编码能力,不支持自行扩展成完全自定义语法。

常见配置主要有以下几种。

some(where (p.eft == allow))

只要所有命中的策略里存在一条 allow,结果就是 true。可以把它理解为“一票赞成制”。这种方式适合默认严格、只要出现明确放行就通过的模型。

!some(where (p.eft == deny))

如果命中的策略中没有任何 deny,那么最终结果就是 true;一旦出现 deny,结果就是 false。这相当于“一票否决制”。

some(where (p.eft == allow)) && !some(where (p.eft == deny))

这种写法要求同时满足两个条件:至少有一条 allow,并且不能存在任何 deny。它表达的是“有人赞成且没人反对才通过”。在需要同时区分显式允许和显式拒绝的模型中,这种组合更常见。

priority(p.eft) || deny

这用于隐式或显式优先级模型。命中的策略会按优先级取其 eft 作为最终结果;如果没有命中,或者命中的 eft 没有给出明确结论,那么结果就是 false。

subjectPriority(p.eft)

它用于基于用户与角色层级关系的优先级模型,重点不只是策略顺序,还包括主体及其继承关系在决策中的优先级。

这部分配置很容易被忽略,因为很多示例都只给出最常见的一种写法。但一旦你的策略中同时存在允许、拒绝、继承和优先级,[policy_effect] 就不再是附属项,而是整套权限逻辑的“裁决规则”。

匹配器:真正执行权限判断的表达式

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

匹配器定义的是具体的匹配规则。m 的值本质上是一个表达式。当调用 enforce(...) 进行决策时,Casbin 会把请求和策略中的变量代入这个表达式,表达式计算结果为真,则表示策略命中;否则就是未命中。

用结构箭头展示 Casbin matchers 如何连接请求、角色关系与策略字段的暖色专栏配图
匹配器如何串起整套模型匹配器把请求定义、策略定义和角色继承真正串成一次权限判断。

上面这个例子可以拆成三部分理解:

  • g(r.sub, p.sub):请求主体与策略主体之间满足角色继承关系;
  • r.obj == p.obj:请求对象与策略对象相等;
  • r.act == p.act:请求动作与策略动作相等。

三者同时成立,这条策略才算命中。

匹配器支持常见算术运算符,如 +, -, *, /,也支持逻辑运算符,例如 &&, ||, !。除此之外,还可以使用内置函数和自定义函数,像示例里的 g(...) 就是一个内置函数。

表达式中的变量名,直接来自前面请求定义和策略定义里的字段名称。因此,这些名称都可以改,但要保证上下文一致。比如前面如果使用的是 uid, uri, method,那么在匹配器中也应该按相同命名来写:

r.uri == p.uri && r.method == p.method

这一点非常关键。Casbin 的 Model 看起来各段彼此独立,实际上真正把它们串起来的就是匹配器:r 从请求定义来,p 从策略定义来,g 是否可用取决于角色定义,命中后的最终布尔结果还要再交给策略效果定义处理。

另外,表达式的解析与运算大多依赖第三方库,这部分对性能有明显影响。也就是说,匹配器不仅决定“能不能匹配对”,还会影响“匹配得快不快”。如果你的权限模型非常复杂,或者请求量较大,这部分就值得结合官方文档进一步评估。

怎么理解 Casbin Model 的整体结构

回头看 Casbin 的 Model,可以把它理解成一套完整的权限判断描述:

  • [request_definition] 定义请求带什么参数;
  • [policy_definition] 定义策略每一列代表什么;
  • [role_definition] 定义角色或主体之间如何继承;
  • [policy_effect] 定义多条策略命中后怎么裁决;
  • [matchers] 定义请求与策略到底如何比较。

这套语法并不限定编程语言,同样适用于 Java、PHP、Python、Go 等其他语言中的 Casbin 实现。

因此,理解 Model 的重点并不是死记每段配置长什么样,而是明确它们之间的配合关系。只要把“请求输入、策略字段、继承关系、裁决规则、匹配表达式”这五层关系想清楚,Casbin 的 Model 就会从“看起来简单但不容易改”变成“结构清楚、能稳定演进”的权限定义方案。

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。