Casbin 在 Slim 中怎么做权限控制:从认证到授权中间件的完整梳理

沈亦白
沈亦白
发布时间:2026-08-25| 93人浏览过
原创

前言

Slim 很适合搭 API,但一旦接口和角色变多,权限判断很容易散落在各个控制器里。Casbin 适合接手这部分工作,关键是把认证、中间件拦截、模型规则和策略配置连成一条清晰链路;看完这个示例,你就能判断它在 Slim Framework 4 里该怎么接、适合用在什么场景。

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 最适合放在中间件层处理。原因很简单:中间件天然能拿到当前请求,也适合在进入业务代码之前统一拦截未授权访问。

用纸面卡片展示 Slim 中从认证到 Casbin 授权再到请求放行或返回 403 的处理链路
请求进入 Slim 后,Casbin 如何做把请求进入应用后的关键判断节点拆开,看清 user、path、method 是如何进入。

这份示例中的授权中间件,会依次取出三个信息:

  • 当前已认证用户 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;
    }
}

这段代码表达的授权流程可以概括为:

  1. 加载 Casbin 的模型文件和策略文件。
  2. 从请求对象中读取认证后的用户。
  3. 取出请求路径和请求方法。
  4. 调用 $e->enforce($user, $uri->getPath(), $action) 判断是否放行。
  5. 如果不允许访问,直接返回 403 和 Unauthorized.。

对于 API 项目来说,这种写法的价值很明确:权限检查不再散落在每个控制器里,而是收口到统一的中间件中。后续新增路由时,只要补充策略,就能沿用同一套判断逻辑。

不过从工程角度看,这段示例更适合作为演示。因为它在每次请求时都会新建一次 Enforcer:

$e = new Enforcer('config/rbac_model.conf', 'config/policy.csv');

如果应用进入生产环境,通常会进一步把 Enforcer 作为服务注册到容器中复用,避免每个请求都重复初始化。

模型文件和策略文件分别负责什么

很多人第一次接触 Casbin,会把模型文件和策略文件混在一起看。实际上这两者分工很清楚:

用并列卡片对比 Casbin 的模型文件与策略文件各自职责,并补充路由匹配关系
Model 与 Policy 各管什么模型定义匹配规则,策略定义访问清单,两者配合后才能把 Slim 路由稳定映射到权限判断。
  • 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 中落地权限系统的团队来说,这种骨架式做法能更快形成统一工程约定。

相关标签:

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

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 算法库。