使用 Casbin 作为 ThinkPHP 的权限控制中间件

沈知远
沈知远
发布时间:2026-08-25| 20人浏览过
原创

前言

Casbin 适合把接口访问规则从控制器里抽出来统一管理,而 ThinkPHP 5.1 在 5.1.6+ 之后已经具备中间件能力,正好能把鉴权放到请求入口。下面按安装、配置、中间件接入和策略验证四个环节梳理一遍,帮助你快速判断这套“用户 + 路由 + 请求方法”的权限控制方式是否适合当前项目。

Casbin 常被用来解决“用户能不能访问某个接口”这类权限判断问题,而 ThinkPHP 5.1 在 5.1.6+ 之后已经具备完整的中间件能力,刚好适合把权限校验放到请求入口统一处理。本文按安装扩展、生成配置、接入中间件、准备策略和验证效果的顺序整理一遍,让你能判断这套做法是否适合自己当前的 ThinkPHP 项目。

为什么在 ThinkPHP 里用 Casbin 做权限控制

PHP-Casbin 是一个开源访问控制框架,支持多种访问控制模型。对于 ThinkPHP 项目来说,它的价值不只是“能做权限”,更重要的是可以把“谁能访问什么 URI、允许什么请求方法”变成可维护的策略数据,而不是散落在控制器里的条件判断。

Think-Casbin 则是专门面向 ThinkPHP 5.1 的扩展包,目的是让 Casbin 在项目中更容易落地。结合中间件后,常见的接口鉴权流程就会比较清晰:先拿到当前登录用户,再读取请求路径和请求方法,最后交给 Casbin 统一判断是否放行。

安装扩展并初始化 Casbin

如果项目还没有创建,可以先生成一个 ThinkPHP 5.1 工程:

安装 Think-Casbin 后生成配置与策略表的初始化流程手绘示意
Casbin 初始化三步到位用纸面流程卡片串起安装、发布资源、迁移建表三步,突出生成的配置文件与策略表。
composer create-project topthink/think=5.1.* tp5

进入 ThinkPHP 项目后,安装 Think-Casbin 扩展:

composer require casbin/think-adapter

安装完成后,需要发布资源文件:

php think casbin:publish

这一步会自动生成两个关键配置文件:

  • config/casbin-basic-model.conf:Casbin 的模型配置文件
  • config/casbin.php:Casbin 在 ThinkPHP 中的运行配置

由于 Think-Casbin 默认把策略(Policy)存储到数据库中,所以还要执行数据迁移:

php think casbin:migrate

执行前要先确认数据库连接信息配置正确。如果你希望单独调整 Casbin 使用的数据库连接或表名,可以直接修改 config/casbin.php。迁移完成后,会自动创建策略表 casbin_rule。

把权限校验放进中间件

ThinkPHP 从 5.1.6+ 开始正式支持中间件,因此最直接的接入方式就是新增一个授权中间件,把每次请求都交给 Casbin 判断。

ThinkPHP 中间件调用 Casbin 判断 401 与 403 的手绘逻辑卡片
中间件里的三层权限判断把登录态、请求信息和 Casbin 判定结果拆成四张卡片,展示 401、403 与放行的区别。

先通过命令生成中间件:

php think make:middleware Authorization

命令会在 application/http/middleware 目录下生成一个 Authorization 中间件。核心逻辑是从 Session 中读取当前用户名,再取出当前请求的 URL 和方法,最后调用 Casbin::enforce() 进行授权判断:

<?php

namespace apphttpmiddleware;

use Casbin;
use thinkfacadeSession;

class Authorization
{
    public function handle($request, Closure $next)
    {
        // 当前登录用户名,这里以session为例
        // $user = Session::get('user_name') ?: 'test_user';
        $user = Session::get('user_name');

        $url = $request->url();
        $action = $request->method();

        // var_dump($user, $uri, $action);die;

        if (!$user){
            return response()->data('Unauthenticated.')->code(401);
        }

        if (!Casbin::enforce($user, $url, $action)) {
            return response()->data('Unauthorized.')->code(403);
        }

        return $next($request);
    }
}

这段逻辑可以看成三层判断:

  • 没有登录用户,直接返回 401,表示未认证
  • 用户已登录,但策略不允许访问当前资源,返回 403
  • 校验通过,继续执行后续请求流程

这种写法的好处是,控制器本身不必重复处理权限判断,接口是否可访问由策略表统一决定。

Casbin 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 && keyMatch2(r.obj, p.obj) && r.act == p.act

这份配置表达的是一个很典型的权限匹配模型:

  • sub 表示访问主体,也就是用户
  • obj 表示访问对象,这里对应请求路径
  • act 表示动作,这里对应 HTTP 方法,如 GET

真正决定效果的是 [matchers] 这一段:

  • r.sub == p.sub:请求用户必须和策略中的用户一致
  • keyMatch2(r.obj, p.obj):请求路径支持按规则匹配,例如 /users/1 可以匹配 /users/:id
  • r.act == p.act:请求方法要和策略定义一致

也就是说,这套配置天然适合“按用户 + 路由 + 请求方法”做接口授权。如果项目权限模型还不复杂,这样的 basic model 已经能覆盖很多后台和 API 场景。

写入策略并绑定路由验证效果

在执行授权验证前,需要先往数据库的 casbin_rule 表里添加策略。示例策略如下:

Casbin 模型与策略表示例如何匹配 users 路由的手绘说明图
策略表如何命中动态路由用对照卡片把策略表、模型字段和 users 路由对应起来,突出 keyMatch2。
id ptype v0 v1 v2
1 p test_user /users GET
2 p test_user /users/:id GET

这两条策略表示:

  • test_user 可以访问 GET /users
  • test_user 可以访问 GET /users/:id

然后再定义路由,并把授权中间件挂到路由组上:

Route::group('users', function () {

    Route::get('', function () {
        return 'Users data.';
    });

    Route::get('/:id', function ($id) {
        return 'User: '.$id;
    });

})->middleware(apphttpmiddlewareAuthorization::class);

接下来,只要在登录后把用户名写入 SESSION,就可以访问 /users 和 /users/1 测试权限效果。整个验证链路是连贯的:

  • Session 中存在当前用户,例如 test_user
  • 请求进入 Authorization 中间件
  • 中间件把用户名、URL、请求方法交给 Casbin
  • Casbin 根据 casbin_rule 和 model 配置决定是否放行

如果路径、请求方法或用户任意一项与策略不匹配,就会返回 403。如果根本没有登录用户,则返回 401。这也解释了为什么 Casbin 非常适合放在接口统一入口处理,而不是零散地写在各个控制器方法中。

落地时值得注意的几点

从这套示例可以看出,ThinkPHP 集成 Casbin 的关键不在代码量,而在于权限数据和请求信息是否能稳定对齐。实际使用时,至少要注意三件事。

1. 用户标识要统一

示例里使用的是 Session::get('user_name'),那么策略表中的 v0 也必须使用同一套用户名标识。如果项目里真正用于鉴权的是用户 ID、角色名或组织标识,就应该统一成同一种值。

2. 路由写法要和匹配规则一致

这里之所以能让 /users/1 命中 /users/:id,依赖的是 keyMatch2。如果你的实际路由风格不同,就要确认 model 里的匹配函数是否仍然适用。

3. 先想清楚是做用户授权还是角色授权

本文示例是直接对用户授权,即把 test_user 直接写进策略里。这种方式适合快速验证,也适合权限规模不大的项目。若后续需要更复杂的 RBAC 设计,再进一步引入角色关系会更合适。

文末原文还提到 Management API 和 RBAC API。如果你后面要做动态增删策略、角色继承或后台权限管理,这两部分通常就是下一步需要补上的能力。

相关标签:

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

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