很多项目在落地权限系统时,都会先用 ACL、RBAC 或 ABAC 的现成模型,但一旦出现“某类用户跳过常规校验”的需求,单靠基础规则就不够用了。PHP-Casbin 这类场景的处理方式并不复杂:把业务判断写成自定义函数,再放进 Matcher 表达式里参与决策。下面就用“超级管理员直接放行”这个常见需求,拆开看配置、注册和验证三个关键步骤。
安装 PHP-Casbin
如果项目还没有引入 Casbin 依赖,先执行安装命令:
composer require casbin/casbin
文章里的示例使用原生 PHP 演示。如果你在 Laravel、ThinkPHP、Yii 等框架中接入,一般可以直接使用对应扩展提供的封装,核心思路并不会变化,重点仍然是围绕 Enforcer 和 Matcher 来组织权限判断。
先准备 Enforcer 和基础模型
在 PHP-Casbin 里,权限决策由 Enforcer 负责执行,而判断逻辑则写在模型配置中。这里先用一个最基础的 basic_model 作为起点:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
这段配置的含义比较直接:请求里的 sub、obj、act 需要和策略中的对应字段全部匹配,才会被放行。也就是说,它适合标准的“谁对什么资源做什么动作”判断,但还没有覆盖“特殊身份绕过规则”的需求。
接着实例化一个决策器:
<?php
require_once './vender/autoload.php';
use CasbinEnforcer;
$enforcer = new Enforcer('path/to/basic_model.conf');
到了这一步,基础的权限校验环境已经具备。接下来要做的,不是重写整套权限逻辑,而是在现有 Matcher 上增加一个额外判断入口。
如何把自定义函数接入 Matcher
假设现在有一个明确需求:如果当前用户是超级管理员,就不再继续比对普通权限策略,直接允许访问。这个场景特别适合用自定义函数来处理,因为它本质上是一个独立的业务判断。

首先要修改模型里的 matchers 表达式:
[matchers]
m = checkIsAdmin(r.sub) || (r.sub == p.sub && r.obj == p.obj && r.act == p.act)
这里新增了一个 checkIsAdmin(r.sub)。它接收请求中的 sub 参数,并返回一个 bool 结果。整个表达式使用了逻辑或 ||,因此只要这个函数返回 true,后面的普通权限匹配就不会继续成为放行门槛。
换句话说,这一行配置把权限决策拆成了两层:
- 第一层,先判断请求主体是否属于特殊身份;
- 第二层,如果不是,再按照原有的
sub / obj / act规则继续校验。
这种写法的价值在于,它对原有策略模型的侵入很小。你不需要把“超级管理员”这类特殊角色硬编码进每一条策略里,只需要在 Matcher 增加一个可复用的业务函数即可。
注册 checkIsAdmin 并实现超级管理员放行
模型里写了函数名之后,还需要在已经实例化好的 Enforcer 上真正注册这个函数。PHP-Casbin 提供了添加函数的方法,传入函数名和方法体即可:
$enforcer->addFunction('checkIsAdmin', function(string $sub): bool {
// 假设如果传入的 sub 等于 root ,即认为是超级管理员
return $sub === 'root';
});
这段代码里,checkIsAdmin 的判断逻辑非常简单:只要传入的 sub 等于 root,就视为超级管理员,函数返回 true。在真实项目里,你当然可以把这部分替换成更贴近业务的数据判断,比如查角色表、查租户配置,或者根据用户上下文判断是否具备全局权限。
但无论内部逻辑怎么变化,接入方式都保持一致:Matcher 只关心函数最终返回的是 true 还是 false。这也是自定义函数最实用的地方,它把复杂业务判断封装在模型之外,同时仍然能自然参与 Casbin 的权限决策流程。
验证结果是否符合预期
函数注册完成后,可以直接通过 enforce 来验证效果:

$res = $enforcer->enforce('aclie', 'data', 'read');
var_dump($res); // false
$res = $enforcer->enforce('root', 'data', 'read');
var_dump($res); // true
从这两次调用可以很清楚地看出差异:
aclie不是超级管理员,checkIsAdmin返回false,因此会继续走后面的常规权限匹配;示例里没有命中策略,所以结果是false。root会让checkIsAdmin返回true,于是直接放行,最终结果就是true。
如果你的目标正是“某个身份跳过普通规则”,那验证时最关键的判断依据就是:同样的资源和动作,在普通用户与特殊用户之间,是否能稳定得到不同的决策结果。只要这一点成立,说明自定义函数已经真正进入了权限链路,而不是停留在单独的业务代码里。
这种做法适合放到哪些场景
这个例子虽然简单,但已经足够说明 PHP-Casbin 自定义函数的用法:把特殊业务逻辑写成函数,通过 addFunction 注册,再在 Matcher 中组合进最终决策表达式。
它特别适合下面这类需求:
- 超级管理员、系统账号、内部服务账号的特殊放行;
- 需要在权限判断前增加额外条件;
- 不想大范围改动已有策略结构,只想局部增强判断逻辑。
对实际项目来说,这种方式的优势不在于“功能多高级”,而在于改动点集中、接入成本低,也不会为了一个特殊规则去大面积侵入现有权限代码。只要 Matcher 设计得清楚,自定义函数就能成为 Casbin 中非常灵活的一层扩展能力。









