简介
本指南面向 DouPHP 框架开发者,系统说明如何编写、注册与使用自定义中间件。内容涵盖:
- 中间件类结构与接口规范(handle 生命周期)
- 基于抽象基类的鉴权中间件实现模式
- 不同应用(前台 front、API、后台 admin)的中间件注册与配置方式
- 从简单日志到复杂业务逻辑的完整示例思路
- 测试、调试技巧与性能优化建议
- 错误处理、异常捕获与响应修改最佳实践
- 中间件与控制器、服务的交互方式
项目结构
DouPHP 将中间件按“端”组织,便于在不同入口独立装配:
- 前台 front/middleware:用户认证、限流、安全头等
- API api/middleware:API 鉴权、限流、安全头等
- 后台 admin/middleware:管理员认证、权限、安全头等
- 核心基础 core/foundation/middleware:统一接口与通用基类
graph TB
subgraph "核心"
MI["MiddlewareInterface"]
AUA["AbstractUserAuthMiddleware"]
ASH["AbstractSecurityHeadersMiddleware"]
end
subgraph "前台"
FUA["front/middleware/UserAuthMiddleware"]
FTH["front/middleware/ThrottleMiddleware"]
FSH["front/middleware/SecurityHeadersMiddleware"]
FCsrf["front/middleware/CsrfMiddleware"]
end
subgraph "API"
AUAi["api/middleware/UserAuthMiddleware"]
ATH["api/middleware/ThrottleMiddleware"]
ASHi["api/middleware/SecurityHeadersMiddleware"]
end
subgraph "后台"
AMW["admin/middleware/AuthMiddleware"]
PMW["admin/middleware/PermissionMiddleware"]
CSF["admin/middleware/CsrfMiddleware"]
SHM["admin/middleware/SecurityHeadersMiddleware"]
end
MI --> AUA
MI --> ASH
AUA --> FUA
AUA --> AUAi
ASH --> FSH
ASH --> ASHi
MI --> AMW
MI --> PMW
MI --> CSF
MI --> SHM
核心组件
- 中间件接口 MiddlewareInterface:定义 handle($next) 生命周期方法,要求放行时 return $next(),不得丢弃下游返回值。
- 用户鉴权基类 AbstractUserAuthMiddleware:模板方法封装“路由段解析 → 策略决策 → public/optional/required 分支 → 身份注入 → work 子策略 → 放行”,子类仅需实现 guard 选择、配置文件路径、拒绝响应等差异点。
- 安全头基类 AbstractSecurityHeadersMiddleware:在管道最前置下发安全响应头(X-Content-Type-Options、X-Frame-Options、Referrer-Policy、Permissions-Policy;HSTS 条件下发)。
架构总览
请求进入 Dispatcher,经中间件管道执行后到达控制器。各端通过 init/middleware.php 声明鉴权模式,结合路由级覆盖,决定是否需要登录或工作端身份。
sequenceDiagram
participant C as "客户端"
participant D as "分发器 Dispatcher"
participant P as "中间件管道"
participant M as "具体中间件"
participant R as "控制器动作"
C->>D : "HTTP 请求"
D->>P : "构建并执行管道"
P->>M : "调用 handle(next)"
M->>M : "校验/记录/设置上下文"
M-->>P : "return $next()"
P->>R : "调用控制器"
R-->>P : "返回 Response/数据"
P-->>C : "输出响应"
详细组件分析
中间件接口与生命周期
- 接口方法 handle($next):负责在合适时机调用下一个处理器或短路返回。
- 关键契约:必须 return $next(),否则控制器响应会被吞掉。
用户鉴权中间件基类(模板方法)
- 职责:根据路由段与配置表决定 public/optional/required,必要时注入用户上下文,并在 required + 无 work 身份时拒绝。
- 扩展点:子类需实现 configFile()、resolveContext()、inject()、hasWorkIdentity()、rejectUnauthenticated()、rejectForbidden()。
- 路由级覆盖:可通过 setRouteParameters([mode]) 强制指定鉴权模式。
flowchart TD
Start(["进入 handle"]) --> Parse["解析路由段 module/sub/action"]
Parse --> Decide{"策略决策<br/>public/optional/required"}
Decide --> |public| Next1["直接放行"]
Decide --> |optional|required| Resolve["解析用户上下文 resolveContext()"]
Resolve --> Ok{"是否登录 ok=true?"}
Ok --> |否| ModeCheck{"模式=required?"}
ModeCheck --> |是| Reject["rejectUnauthenticated()"]
ModeCheck --> |否| Next2["放行匿名"]
Ok --> |是| Inject["注入上下文 inject()"]
Inject --> Work{"work_required 且无工作身份?"}
Work --> |是| Forbid["rejectForbidden()"]
Work --> |否| Next3["放行"]
前台用户认证中间件
- 继承自抽象基类,使用 auth('front') guard,拒绝时支持跳转登录页或 JSON 401(XHR from=js)。
- 配置文件路径:FRONT_PATH . 'init/middleware.php'。
API 用户认证中间件
- 继承自抽象基类,使用 auth('api') guard,从 Authorization: Bearer 提取 token 解析登录态。
- 拒绝时直接发送 JSON 错误(401/403)并 exit。
- 配置文件路径:API_PATH . 'init/middleware.php'。
后台认证中间件
- 实现 MiddlewareInterface,通过 auth('admin')->restoreFromSession(ip) 恢复登录态。
- 未登录抛出 HttpResponseException 重定向至登录页;已登录则放行。
安全响应头中间件
- 基类在管道最前置下发安全头,仅对命中路由生效。
- 三端薄壳子类可复用该行为。
鉴权模式配置(单中间件多模式)
- 前台与 API 均通过 init/middleware.php 中的 auth_modes 与 work_required 声明模块级鉴权级别与工作端要求。
- 支持 module / module/action / module/sub / module/sub/action 多级键匹配。
- 新增模块必须在配置中显式登记,避免静默公开。
依赖关系分析
- 中间件依赖 request()/auth() 等全局辅助获取请求与守卫。
- 鉴权中间件依赖 UserAuthPolicy(由基类内部使用)进行策略决策。
- 分发器 Dispatcher 负责在管道中实例化控制器并执行,不关心中间件细节。
classDiagram
class MiddlewareInterface {
+handle(next) mixed
}
class AbstractUserAuthMiddleware {
-authModes array
-workRequired array
-routeModeOverride string?
+setRouteParameters(params) void
+handle(next) mixed
#configFile() string
#resolveContext() array
#inject(context) void
#hasWorkIdentity() bool
#rejectUnauthenticated() void
#rejectForbidden() void
}
class FrontUserAuthMiddleware
class ApiUserAuthMiddleware
class AdminAuthMiddleware
class Dispatcher {
+run(plan, container) mixed
}
MiddlewareInterface <|.. AbstractUserAuthMiddleware
AbstractUserAuthMiddleware <|-- FrontUserAuthMiddleware
AbstractUserAuthMiddleware <|-- ApiUserAuthMiddleware
MiddlewareInterface <|.. AdminAuthMiddleware
Dispatcher ..> MiddlewareInterface : "管道执行"
性能考虑
- 中间件应轻量:避免在 handle 中进行重型 I/O;将耗时操作下沉到服务层或异步任务。
- 尽早短路:鉴权失败立即返回,减少后续中间件与控制器开销。
- 缓存友好:对只读上下文(如用户信息)尽量复用 Guard 缓存,避免重复查询。
- 安全头仅在命中路由下发,减少不必要开销。
- 合理拆分中间件:单一职责,便于按需启用与测试。
故障排查指南
- 响应被吞掉:检查是否忘记 return $next(),或捕获了下游响应却未返回。
- 鉴权误拦截:核对 init/middleware.php 中对应模块/动作的 auth_modes 是否正确;必要时使用路由级覆盖。
- 工作端权限不足:确认 work_required 列表是否包含当前模块;检查 hasWorkIdentity 判定逻辑。
- 前端 XHR 登录超时:前台 rejectUnauthenticated 会返回带 jump_url 的 JSON;确保前端能处理该字段并重定向。
- 安全头未生效:确认请求命中路由且在 headers_sent() 之前;HTTPS 下 HSTS 需要显式开启。
结论
DouPHP 的中间件体系以统一接口与可复用的鉴权基类为核心,配合各端独立的鉴权配置,实现了高内聚、低耦合的可插拔横切能力。遵循 handle 契约、合理使用公共基类、在 init/middleware.php 中显式声明鉴权模式,即可快速构建稳定可靠的自定义中间件。
附录
开发步骤清单
- 新建中间件类,实现 MiddlewareInterface 或继承 AbstractUserAuthMiddleware/AbstractSecurityHeadersMiddleware。
- 在 handle 中完成前置处理,务必 return $next() 放行。
- 如需鉴权:
- 在前台或 API 端创建中间件,实现配置文件路径、上下文解析、注入与拒绝逻辑。
- 在对应 init/middleware.php 的 auth_modes 中登记模块/动作的鉴权级别。
- 在路由或管道中注册中间件(由框架调度),并通过路由级参数覆盖鉴权模式。
- 编写单元测试:模拟请求、断言响应状态码与头部、验证是否调用 $next()。
- 调试技巧:
- 在 handle 前后记录请求 ID、耗时、关键上下文。
- 使用浏览器网络面板或 curl 查看响应头与状态码。
- 针对 XHR 场景检查 from=js 分支。