文档目录
后台路由系统

简介

本文件面向 DouPHP 后台路由系统,系统性说明 AdminResolver 的安全路由解析机制、中间件链执行顺序、控制器到视图的映射方式、以及后台 API 路由设计与错误响应规范。文档同时提供后台路由配置模板与自定义权限规则的实现指引,帮助开发者在安全、可维护的前提下扩展后台功能。

重大更新 导航系统已重构为集中式管理架构,移除了 RouteNavDsl 声明式 DSL,改用 AdminMenuRegistry 注册表和 AdminNavResolver 解析器实现配置驱动的导航高亮系统。

项目结构

后台入口通过 admin/index.php?route=... 进入,由 Router 负责调度,交由 AdminResolver 解析为分发计划(DispatchPlan),再经 Dispatcher 在中间件管道中执行,最终落到具体控制器方法并渲染视图或返回响应。

graph TB
A["请求<br/>admin/index.php?route=..."] --> B["Router::dispatch()"]
B --> C["AdminResolver::resolve()"]
C --> D["BackendDeclaredMatcher 匹配<br/>生成 DispatchPlan"]
D --> E["Dispatcher::run()<br/>中间件链执行"]
E --> F["控制器方法<br/>BaseController/view()"]
F --> G["视图模板渲染<br/>dou_msg.htm / 业务模板"]

核心组件

  • 路由解析器:AdminResolver,负责将 URL 解析为控制器、动作、参数与中间件栈,并注入表单目标等全局视图变量。
  • 路由调度器:Router,统一处理未命中与方法不允许的情况,并委托中央调度器执行。
  • 中间件链:安全头、代理信任、认证、权限、CSRF、工作台变量注入。
  • 控制器基类:BaseController,统一视图响应、消息提示、删除二次确认、布尔切换等通用行为。
  • 授权判定:AdminGate,基于管理员类型与 action_list 白名单进行模块级访问控制。
  • 消息响应:AdminMessageResponder,统一后台提示页与跳转逻辑。
  • 菜单注册表:AdminMenuRegistry,集中管理后台菜单结构和匹配规则。
  • 导航解析器:AdminNavResolver,根据当前路由名计算子菜单和侧栏的高亮状态。
  • 菜单服务:AdminMenuService,提供框架基础菜单键列表。

架构总览

后台采用"声明式路由 + 分层中间件 + 集中式菜单管理"的架构。URL 命中后,AdminResolver 根据命中条目组装中间件栈,默认栈包含安全头、代理信任、认证、权限、CSRF、工作台注入;可通过路由级 withoutMiddleware 精确豁免。新的集中式菜单管理系统通过 AdminMenuRegistry 定义菜单结构,AdminNavResolver 根据当前路由名计算高亮状态。

sequenceDiagram
participant U as "浏览器"
participant R as "Router"
participant AR as "AdminResolver"
participant M as "中间件链"
participant C as "控制器"
participant V as "视图"
U->>R : GET /admin/?route=article/edit
R->>AR : resolve(request, container)
AR-->>R : DispatchPlan(控制器, 方法, 参数, 中间件)
R->>M : Dispatcher : : run(plan)
M->>M : 安全头 -> 代理信任 -> 认证 -> 权限 -> CSRF -> 工作台
M->>C : 调用控制器方法
C->>V : view()/message()->respond()
V-->>U : HTML/JSON/重定向

详细组件分析

AdminResolver 安全路由解析

  • 解析流程:读取 route 字符串,使用后端声明匹配器按 URL 命中、优先级与 HTTP 方法过滤,产出 DispatchPlan。
  • 安全策略:默认中间件栈 secure-by-default;未匹配返回 notFound,方法不被接受返回 methodNotAllowed。
  • 视图装配:create/edit 时自动注入 form_action 与 form_method,使模板统一使用 {$form_action} 与 {$form_method}。
flowchart TD
Start(["开始"]) --> ReadRoute["读取 route 字符串"]
ReadRoute --> Match["后端声明匹配器匹配"]
Match --> |未命中| NotFound["返回 notFound"]
Match --> |方法不允许| MethodNotAllow["返回 methodNotAllowed"]
Match --> BuildPlan["构建 DispatchPlan<br/>设置基础URL/路由/参数"]
BuildPlan --> AssignForm["create/edit 注入表单目标"]
AssignForm --> ReturnPlan["返回计划给调度器"]

中间件链执行顺序与安全职责

  • 默认顺序:安全头 → 代理信任 → 认证 → 权限 → CSRF → 工作台变量注入。
  • 认证检查:从会话恢复登录态,未登录重定向至登录页。
  • 权限验证:依据管理员类型与 action_list 白名单判定模块访问;超级管理员放行;manager 自编辑特判。
  • CSRF 校验:统一令牌模型 static_admin;匿名重置密码走一次性令牌 password_reset;部分 GET 续跑链接也校验。
  • 安全头:继承基类实现,设置基线安全响应头。
  • 工作台注入:在认证与权限通过后注入 global_admin、workspace、unum 等视图变量。
sequenceDiagram
participant MW as "中间件链"
participant Auth as "认证"
participant Perm as "权限"
participant CSRF as "CSRF"
participant WS as "工作台"
MW->>Auth : handle(next)
Auth-->>MW : 通过/重定向登录
MW->>Perm : handle(next)
Perm-->>MW : 通过/重定向首页
MW->>CSRF : handle(next)
CSRF-->>MW : 通过/拒绝并重定向
MW->>WS : handle(next)
WS-->>MW : 注入视图变量

控制器到视图的映射与响应

  • 视图渲染:BaseController::view 统一构造 ViewResponse,合并 layoutVars 与 AI 工具栏配置,确保按钮链接绝对化。
  • 导航兜底:BaseController::navFallbackVars() 提供向后兼容的 cur 变量,确保旧模板继续工作。
  • 消息提示:BaseController::respondDeleteResult 支持删除二次确认与 AJAX 布尔切换;AdminMessageResponder 统一 dou_msg.htm 提示页。
  • 模板变量:AdminWorkspaceMiddleware 注入 global_admin、workspace、unum;AdminResolver 在 create/edit 注入 form_action/form_method。
classDiagram
class BaseController {
+view(template, data, status)
+layoutVars() array
+navFallbackVars() array
+respondDeleteResult(result) Response
+respondToggle(request, value, message, backUrl) Response
}
class AdminMessageResponder {
+respond(text, url, out, time, check, btnValue, checkMethod) Response
}
class AdminWorkspaceMiddleware {
+handle(next) mixed
}
BaseController --> AdminMessageResponder : "消息响应"
AdminWorkspaceMiddleware --> BaseController : "注入视图变量"

后台路由配置模板与示例

  • 命名空间分组:使用 Route::name('admin.')->group(...) 组织路由,便于生成 admin.* 路由名。
  • 免登组:登录相关路由通过 withoutMiddleware(['auth','permission','workspace']) 豁免认证与权限。
  • 独立 POST 路由:login/post 单独声明并豁免 csrf,避免匿名提交无令牌问题。
  • 首页路由:index 模块提供 GET index 与若干 POST 子动作。
flowchart LR
A["Route::name('admin.')"] --> B["group('login', LoginController)"]
B --> C["prefix('login')"]
C --> D["withoutMiddleware(['auth','permission','workspace'])"]
D --> E["GET ['index','password_reset']"]
D --> F["POST ['logout','password_reset_post']"]
A --> G["post('login/post', LoginController, 'post', 'login')"]
G --> H["withoutMiddleware(['auth','permission','workspace','csrf'])"]
A --> I["resource('ai', AiController)"]
I --> J["prefix('ai/generate/batch')"]

登录流程时序

sequenceDiagram
participant U as "管理员"
participant L as "LoginController"
participant S as "AdminLoginFlow"
participant M as "AdminMessageResponder"
U->>L : GET /admin/?route=login
L-->>U : 渲染 login.htm
U->>L : POST /admin/?route=login/post
L->>S : handle(data, ip)
S-->>L : 成功/失败
alt 失败
L->>M : respond(错误信息, 登录页, 'out')
M-->>U : dou_msg.htm
else 成功
L-->>U : 重定向到后台首页
end

权限判定与操作审计

  • 权限判定:PermissionMiddleware 调用 AdminGate::canAccess,依据管理员类型与 action_list 白名单判定模块访问;子资源模块通过别名表归一到父模块。
  • 审计日志:业务服务层在关键操作处调用审计接口记录管理员操作(例如创建、更新等)。
flowchart TD
Start(["权限检查入口"]) --> TypeCheck{"是否超级管理员?"}
TypeCheck --> |是| Allow["放行"]
TypeCheck --> |否| SelfEdit{"是否 manager 自编辑?"}
SelfEdit --> |是| Allow
SelfEdit --> |否| Alias["子资源归一化到父模块"]
Alias --> CheckList{"action_list 包含当前模块?"}
CheckList --> |是| Allow
CheckList --> |否| Deny["重定向首页/拒绝"]

集中式菜单管理系统新特性

AdminMenuRegistry 菜单注册表

AdminMenuRegistry 类作为后台菜单的集中式注册表,定义了所有子菜单族和侧栏节点的结构与匹配规则。

  • 子菜单族:通过 subMenus() 方法返回核心族(manager、miniprogram、site_home)和模块声明文件的合并结果。
  • 侧栏节点:通过 sideNodes() 方法定义按渲染节分组的侧栏节点(top、main、item、bot、header)。
  • 条件判断:passWhen() 方法支持 sign、sign_not、feature、not_pure_mode 等多种条件判断。
  • 会员中心:userCenterFamilies() 方法定义会员中心入口模块到子菜单族的映射关系。
classDiagram
class AdminMenuRegistry {
+subMenus() array
+sideNodes() array
+userCenterFamilies() array
+passWhen(array $when) bool
}
class AdminNavResolver {
+resolve($routeName, array $overrides) array
+emptyNav() array
+routeMatches($routeName, $pattern) bool
}
AdminMenuRegistry --> AdminNavResolver : "被解析器使用"

AdminNavResolver 导航解析器

AdminNavResolver 类负责根据当前路由名计算子菜单和侧栏的高亮状态,是导航系统的核心解析器。

  • 解析契约:resolve() 方法返回包含 route_name、sub_menu、side、side_active_id 的标准契约。
  • 匹配算法:使用 patternSpecificity() 方法计算模式匹配权重,支持段边界通配和尾 .* 兼容。
  • 竞争裁决:matchSpecificity() 方法处理多模式竞争,以最长字面前缀为 specificity 胜出。
  • 侧栏计算:resolveSide() 方法计算所有侧栏节点的布尔状态,支持页面级覆盖。
flowchart TD
Start(["AdminNavResolver::resolve()"]) --> GetRoute["获取路由名"]
GetRoute --> ExpandItems["展开所有族项"]
ExpandItems --> CalculateSpec["计算匹配特异性"]
CalculateSpec --> DetermineWinner["确定获胜者"]
DetermineWinner --> BuildSubMenu["构建子菜单"]
BuildSubMenu --> ResolveSide["解析侧栏状态"]
ResolveSide --> ReturnNav["返回导航契约"]

菜单配置最佳实践

新的集中式菜单管理系统提供了更清晰和可维护的配置方式:

  • 声明式配置:在 AdminMenuRegistry 中集中定义菜单结构,提高可读性和可维护性。
  • 条件渲染:通过 when 字段实现条件渲染,支持多种配置开关。
  • 动态扩展:模块可以通过 admin/nav/&lt;module>.php 文件声明自己的子菜单族。
  • 向后兼容:保持与现有模板的兼容性,无需修改现有代码。

依赖关系分析

  • Router 依赖 AdminResolver 与 Dispatcher。
  • AdminResolver 依赖 BackendDeclaredMatcher、MethodResolver、MiddlewareRegistry、View。
  • 中间件之间形成链式依赖:认证→权限→CSRF→工作台注入。
  • 控制器依赖 BaseService、Facade、TemplateRendererInterface 等基础设施。
  • 菜单系统依赖:AdminMenuRegistry 定义菜单结构,AdminNavResolver 解析导航状态。
graph LR
Router["Router"] --> Resolver["AdminResolver"]
Resolver --> Matcher["BackendDeclaredMatcher"]
Resolver --> MR["MethodResolver"]
Resolver --> MWReg["MiddlewareRegistry"]
Resolver --> View["View"]
Resolver --> Plan["DispatchPlan"]
Router --> Disp["Dispatcher"]
Disp --> Middlewares["中间件链"]
Middlewares --> Controller["控制器"]
Controller --> Template["视图引擎"]
MenuRegistry["AdminMenuRegistry"] --> NavResolver["AdminNavResolver"]
NavResolver --> MenuService["AdminMenuService"]

性能考虑

  • 中间件最小化:仅在必要时挂载,利用 withoutMiddleware 精准豁免,减少无效校验。
  • 视图变量懒加载:layoutVars 仅在真正渲染视图时执行,避免不必要的 Session 读取与计算。
  • 权限判定优化:AdminGate 对子资源进行别名归一化,减少重复查库与判断。
  • 令牌复用:CSRF 使用共享静态令牌,降低频繁生成开销。
  • 菜单缓存:AdminMenuRegistry::subMenus() 使用请求级缓存,避免重复扫描文件系统。
  • 解析器优化:AdminNavResolver 采用纯函数实现,结果仅取决于入参,可重复调用。

故障排查指南

  • 未命中路由:Router 会重定向到后台首页并携带 page_wrong 提示;检查路由声明是否正确、HTTP 方法与路径是否匹配。
  • 方法不允许:返回 405 并附带 Allow 头;核对路由声明的 get/post 限制。
  • 认证失败:AuthMiddleware 抛出异常并重定向登录页;检查会话是否有效、IP 是否变化导致会话失效。
  • 权限不足:PermissionMiddleware 重定向首页;检查管理员类型与 action_list 白名单、子资源别名映射。
  • CSRF 失败:CsrfMiddleware 拒绝并提示页面过期;刷新页面或重新登录,检查表单是否携带正确令牌。
  • 提示页异常:AdminMessageResponder 兜底超时秒数,避免 meta refresh 被忽略;检查传入时间与 URL 是否为空。
  • 菜单高亮异常:检查 AdminMenuRegistry 中的 match 配置是否正确,确认 AdminNavResolver 的解析逻辑。
  • 条件渲染问题:检查 when 字段配置,确认 passWhen() 方法的条件判断是否符合预期。

结论

DouPHP 后台路由系统以声明式路由与分层中间件为核心,实现了安全默认、细粒度豁免、统一视图响应与一致的错误提示。通过 AdminResolver 的路径解析、中间件链的职责分离、BaseController 的响应抽象与 AdminGate 的权限判定,系统在安全性与可维护性之间取得平衡。重大更新的集中式菜单管理系统进一步增强了后台用户体验,通过 AdminMenuRegistry 注册表和 AdminNavResolver 解析器,实现了配置驱动的导航高亮系统,消除了传统控制器中的手动状态声明,使后台界面更加直观易用,同时保持了向后兼容性。 开发者可按本文档提供的模板与指南,快速扩展后台功能并遵循统一的规范。

附录

后台路由配置模板

  • 命名空间分组:使用 Route::name('admin.')->group(...) 组织路由。
  • 前缀与作用域:通过 prefix 限定作用域,结合 get/post 声明动作。
  • 中间件豁免:使用 withoutMiddleware 对无需认证的公开路由进行豁免。
  • 示例参考:
    • 登录模块:login.php:33-42
    • 首页模块:index.php:30-36

自定义权限规则实现指南

  • 新增子资源别名:在 AdminGate::$subModuleAliases 中登记子资源到父模块的映射,确保鉴权透明继承。
  • 扩展白名单:在管理员 action_list 中增加新模块名,或通过超级管理员类型放行。
  • 特殊场景:如需针对特定动作放行(如 manager 自编辑),可在 AdminGate 中追加判断逻辑。

后台 API 路由设计与错误响应格式

  • 设计原则:RESTful 风格,明确 GET/POST 语义;通过路由声明集中管理;对敏感接口启用认证与权限中间件。
  • 错误响应:
    • 未命中:重定向首页并携带 page_wrong 提示。
    • 方法不允许:返回 405 并附带 Allow 头。
    • 认证失败:重定向登录页。
    • 权限不足:重定向首页。
    • CSRF 失败:提示页面过期并重定向。
  • 统一消息:使用 AdminMessageResponder 输出 dou_msg.htm 提示页,保证一致的交互体验。

集中式菜单配置最佳实践

  • 声明式配置:优先在 AdminMenuRegistry 中进行集中式配置,提高可读性和可维护性。
  • 条件渲染:合理使用 when 字段实现条件渲染,支持多种配置开关。
  • 动态扩展:模块可以通过 admin/nav/&lt;module>.php 文件声明自己的子菜单族。
  • 测试建议:在开发环境中检查 AdminNavResolver::resolve() 的返回值,确保导航高亮符合预期。
  • 配置示例:
    • 基本菜单项:array('key' => 'list', 'name' => 'menu_list', 'link' => 'admin.list', 'match' => array('admin.list.*'))
    • 条件菜单项:array('key' => 'data', 'name' => 'data', 'link' => 'admin.data', 'match' => array('admin.data.*'), 'when' => array('feature' => 'data'))
    • 排除规则:array('exclude' => array('admin.work.*'))
添加日期:2026-10-05