职责链实现权限中间件的核心是每个校验器只负责自身逻辑并统一返回null(放行)或响应,通过setnext串联且必须调用$next->handle();需避免die/exit、空next调用、过度io及上下文冗余。

PHP中用职责链实现权限中间件的核心思路
职责链模式不是为“炫技”设计的,而是解决这类问题:当一个请求要过登录校验→角色白名单→接口级RBAC→数据行级权限四道关,且每道关可能跳过、短路或记录日志时,硬编码if-else会迅速失控。关键在于让每个校验器只关心“自己该不该处理”和“不通过时怎么响应”,不耦合其他环节。
定义统一的Handler接口和基础抽象类
所有校验器必须遵循同一契约,否则链无法串联。重点是handle()返回null表示放行,返回Response对象(或数组)表示拦截,且必须调用$this->next->handle()交给下一环——漏掉这句,链就断了。
interface AuthHandler
{
public function handle(array $context): ?array;
public function setNext(AuthHandler $next): void;
}
abstract class AbstractAuthHandler implements AuthHandler
{
protected ?AuthHandler $next = null;
public function setNext(AuthHandler $next): void
{
$this->next = $next;
}
}
常见错误:在handle()里直接die()或exit,导致后续处理器完全无机会执行;或者忘记检查$this->next是否为null就调用handle(),触发PHP警告。
具体校验器的实现要点(以JWT Token校验为例)
TokenAuthHandler只做三件事:解析Header里的Authorization、验证JWT签名和有效期、把用户ID塞进$context['user_id']。它不决定“有没有权限访问订单接口”,只决定“这个token本身是否合法”。一旦失败,立刻返回401响应;成功则必须交棒给下一个处理器。
- 别在
handle()里查数据库——用缓存或提前注入好的$userRepo实例,避免链中多次IO - 从
$context读取参数而非$_SERVER或$_GET,保证可测试性 - 如果token过期但refresh_token有效,可以返回
['status' => 'refresh_required']让上层统一跳转,而不是直接拒绝
组装链与在框架中挂载的实际方式
在Laravel中,通常在app/Http/Middleware/AuthChain.php里构建链并调用:$handler->handle(['request' => $request, 'route' => $route])。注意:不要在构造函数里new一堆Handler,而应通过容器解析,否则无法注入依赖(比如UserRepository)。
性能影响点:每增加一个Handler,就多一次函数调用+条件判断。若项目只有登录校验和角色检查,用两个if更轻量;但一旦涉及动态策略(如“财务组只能看自己部门数据”),职责链的可维护性优势立刻凸显。
容易被忽略的是上下文透传——比如RoleAuthHandler需要知道当前请求的route name,但TokenAuthHandler根本不需要。别把所有信息都塞进$context,按需传递,否则后期调试时根本分不清哪个Handler往里写了什么。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











