Slim 适合快速搭建轻量 API,但项目一旦进入多角色、多路由场景,权限判断很容易散落在控制器里,后续维护也会越来越重。本文用 Casbin 在 Slim Framework 4 中的接入示例,把“先认证、再授权”的链路拆开说明,并结合中间件、模型配置和策略文件,帮助你判断这套做法适不适合现有项目。
先把认证和授权分开
Casbin 解决的是“这个用户是否有权限访问当前资源”,前提是系统已经知道“当前用户是谁”。因此在 Slim 中接入 Casbin 时,通常先做认证,再做授权。
这份示例里使用的是 HTTP Basic Authentication,对应的中间件来自 slim-basic-auth。安装命令如下:
composer require tuupola/slim-basic-auth
认证中间件完成的事情很直接:校验账号密码,并把认证后的用户信息写入请求对象,供后续授权中间件继续使用。
$app->add(new HttpBasicAuthentication([
'users' => [
'root' => 't00r',
'somebody' => 'passw0rd',
],
'before' => function ($request, $arguments) {
return $request->withAttribute('user', $arguments['user']);
},
]));
这里有两个关键点:
- 用户身份不是在 Casbin 中识别的,而是由认证中间件写入
user属性。 - 后面的授权判断会直接读取这个
user,再结合请求路径和 HTTP Method 做决策。
这样拆分后,认证逻辑和权限逻辑各自独立,后面如果你要把 Basic Auth 换成 JWT、Session 或其他登录方案,Casbin 的授权部分通常不需要大改。
Casbin 授权中间件怎么工作
在 Slim 里,Casbin 最适合放在中间件层处理。原因很简单:中间件天然能拿到当前请求,也适合在进入业务代码之前统一拦截未授权访问。

这份示例中的授权中间件,会依次取出三个信息:
- 当前已认证用户
user - 当前请求的
uri - 当前请求方法
method
然后把这三个值交给 Casbin 的 enforce() 做权限决策。
namespace AppMiddleware;
use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpServerRequestHandlerInterface as RequestHandler;
use SlimPsr7Response;
use CasbinEnforcer;
class Authorization
{
/**
* Authorization middleware invokable class.
*
* @param ServerRequest $request PSR-7 request
* @param RequestHandler $handler PSR-15 request handler
*
* @return Response
*/
public function __invoke(Request $request, RequestHandler $handler): Response
{
$e = new Enforcer('config/rbac_model.conf', 'config/policy.csv');
$user = $request->getAttribute('user');
$uri = $request->getUri();
$action = $request->getMethod();
if ($user && !$e->enforce($user, $uri->getPath(), $action)) {
$response = new Response();
$response->withStatus(403)->getBody()->write('Unauthorized.');
return $response;
}
$response = $handler->handle($request);
return $response;
}
}
这段代码表达的授权流程可以概括为:
- 加载 Casbin 的模型文件和策略文件。
- 从请求对象中读取认证后的用户。
- 取出请求路径和请求方法。
- 调用
$e->enforce($user, $uri->getPath(), $action)判断是否放行。 - 如果不允许访问,直接返回
403和Unauthorized.。
对于 API 项目来说,这种写法的价值很明确:权限检查不再散落在每个控制器里,而是收口到统一的中间件中。后续新增路由时,只要补充策略,就能沿用同一套判断逻辑。
不过从工程角度看,这段示例更适合作为演示。因为它在每次请求时都会新建一次 Enforcer:
$e = new Enforcer('config/rbac_model.conf', 'config/policy.csv');
如果应用进入生产环境,通常会进一步把 Enforcer 作为服务注册到容器中复用,避免每个请求都重复初始化。
模型文件和策略文件分别负责什么
很多人第一次接触 Casbin,会把模型文件和策略文件混在一起看。实际上这两者分工很清楚:

rbac_model.conf定义“按什么规则判断权限”policy.csv定义“谁能访问什么资源”
Model 文件:定义权限匹配规则
示例中的 config/rbac_model.conf 内容如下:
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && r.act == p.act
这套配置里最值得关注的是最后一段 matchers:
g(r.sub, p.sub)表示主体和角色的关联匹配,适合做 RBAC。keyMatch2(r.obj, p.obj)表示路径匹配支持参数化写法。r.act == p.act表示请求方法必须一致,例如GET和POST会被当成不同权限。
其中 keyMatch2 很适合 Web 路由场景。它可以把请求路径和策略中的模式路径进行匹配,例如把带参数的资源访问规则写得更紧凑。
Policy 文件:定义允许访问的资源
示例中的 config/policy.csv 内容如下:
p, root, /, GET
p, root, /users, GET
p, root, /users/:id, GET
这三条策略表达的是:用户或角色 root 可以通过 GET 方法访问以下资源:
//users/users/:id
这里的 /users/:id 能和实际路由参数匹配,正是因为模型中使用了 keyMatch2。这让 Casbin 在 RESTful API 中非常顺手,不需要为每个具体 ID 单独写一条策略。
换句话说,模型决定“怎么比”,策略决定“比什么”。把这两层分开之后,权限系统就更容易维护:当访问规则变化时,优先改策略;当判断方式变化时,再去调整模型。
路由和权限规则如何对应
有了认证和授权中间件之后,还需要看路由定义是否和策略保持一致。示例中的路由如下:
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello Casbin !');
return $response;
});
$app->group('/users', function (Group $group) {
$group->get('', ListUsersAction::class);
$group->get('/{id}', ViewUserAction::class);
});
这组路由和前面的策略文件是一一对应的:
$app->get('/')对应p, root, /, GET$group->get('')对应p, root, /users, GET$group->get('/{id}')对应p, root, /users/:id, GET
要让这套权限判断稳定工作,核心不是“路由写得多复杂”,而是“路由模式和策略模式能不能稳定对上”。Slim 中使用的是 /{id},而策略里写的是 :id,Casbin 通过匹配函数完成这层兼容,因此不必把每个变量路径展开成硬编码规则。
这也意味着,当你继续扩展接口时,应该优先建立一套统一规范:
- 资源路径保持 RESTful 风格
- 请求方法严格区分
GET、POST、PUT、DELETE - 策略文件中的路径模式与应用路由风格保持一致
这样后续无论是新增后台接口,还是补角色权限,都会更容易核对和排查。
这套集成方式适合什么项目
从示例可以看出,Casbin 在 Slim 里的接入并不复杂:认证中间件确认身份,授权中间件统一拦截,请求路径和方法作为决策输入,模型与策略文件分别管理规则和权限。
这种结构尤其适合几类项目:
- 路由数量逐渐增多的 API 服务
- 需要按角色控制访问范围的后台系统
- 希望把权限逻辑从控制器中抽离出来的 PHP 应用
如果你只是做一个极小型、几乎没有角色差异的内部工具,直接在代码里做简单判断也许更快;但只要接口开始增长,Casbin 这种把授权规则独立出来的方式,维护成本通常会更低。
文中提到的 Casbin skeleton application with Slim Framework 4,本质上就是为了把这套接入流程提前搭好,减少新项目重复配置的时间。对于准备在 Slim Framework 4 中落地权限系统的团队来说,这种骨架式做法能更快形成统一工程约定。













