文档目录
资源权限控制

简介

本文件面向 DouPHP 后台与 API 的资源权限控制系统,系统性说明以下能力:

  • 请求拦截与权限检查流程(中间件链)
  • 后台模块访问授权判定(AdminGate::canAccess)
  • 菜单权限控制(动态菜单生成与权限过滤)
  • 操作权限控制(按钮级与 API 接口级)
  • 数据权限控制方案(基于用户的数据隔离)
  • 权限配置的管理界面与使用方法

项目结构

DouPHP 的权限体系由“认证中间件 + 授权中间件 + 授权服务 + 路由/菜单服务 + 前端/小程序校验”共同构成。后台入口在 admin 目录,API 鉴权在 api 目录,小程序侧通过独立服务调用后端权限接口进行降级处理。

graph TB
A["管理员浏览器"] --> B["后台认证中间件<br/>AuthMiddleware"]
B --> C["后台权限中间件<br/>PermissionMiddleware"]
C --> D["授权判定服务<br/>AdminGate"]
D --> E["控制器/业务逻辑"]
F["小程序/前端"] --> G["API 鉴权中间件<br/>UserAuthMiddleware"]
G --> H["API 路由/控制器"]
I["菜单服务<br/>AdminMenuService"] --> J["管理界面渲染"]

核心组件

  • 后台认证中间件:负责从会话恢复管理员登录态,未登录则重定向到登录页。
  • 后台权限中间件:在认证通过后,提取当前模块、动作和目标 ID,调用 AdminGate 进行访问授权判定;无权时重定向回后台首页。
  • 授权判定服务 AdminGate:承载 canAccess 判断逻辑,支持超级管理员放行、子资源别名归一化、自身资料编辑放行等策略。
  • 管理员维护服务 ManagerService:提供管理员权限勾选列表、模块选项、日志查询等能力,是权限配置界面的支撑服务。
  • API 鉴权中间件:按路由配置的鉴权模式(public/optional/required/work_required)解析登录态并执行工作端身份叠加校验。
  • 小程序权限校验:调用 work.permission 接口进行工作台权限校验,失败时降级返回首页。

架构总览

后台请求进入后,先经 AuthMiddleware 完成会话恢复,再经 PermissionMiddleware 进行模块级授权判定;API 请求根据 api/init/middleware.php 中声明的 auth_modes 选择鉴权模式,并由 UserAuthMiddleware 统一处理。

sequenceDiagram
participant U as "管理员"
participant AM as "AuthMiddleware"
participant PM as "PermissionMiddleware"
participant AG as "AdminGate"
participant CT as "控制器"
U->>AM : 发起后台请求
AM->>AM : 恢复会话登录态
AM-->>U : 未登录则跳转登录页
AM-->>PM : 已登录继续管道
PM->>PM : 读取 routeModule/routeAction/id
PM->>AG : canAccess(admin, cur, action, targetId)
AG-->>PM : 允许/拒绝
PM-->>CT : 允许则执行业务
PM-->>U : 拒绝则重定向后台首页

详细组件分析

后台权限中间件 PermissionMiddleware

  • 职责:在认证通过后,提取当前路由模块、动作与目标 ID,调用 AdminGate 进行访问判定;无权限时抛出响应异常并重定向至后台首页。
  • 关键点:
    • 若未获取到管理员上下文,直接重定向到登录页。
    • 若当前模块为空,重定向到后台根路径。
    • 默认 action 为 index,targetId 来自请求参数 id。
    • 最终依据 AdminGate::canAccess 的结果决定是否放行。
flowchart TD
Start(["进入 PermissionMiddleware"]) --> CheckAdmin["检查管理员上下文"]
CheckAdmin --> |为空| RedirectLogin["重定向到登录页"]
CheckAdmin --> |存在| ReadRoute["读取 module/action/id"]
ReadRoute --> EmptyModule{"module 是否为空?"}
EmptyModule --> |是| RedirectHome["重定向到后台首页"]
EmptyModule --> |否| GateCall["调用 AdminGate::canAccess"]
GateCall --> Allowed{"是否允许?"}
Allowed --> |否| RedirectHome
Allowed --> |是| Next["继续后续处理器"]

授权判定服务 AdminGate

  • 职责:承载 canAccess 判断逻辑,决定当前管理员能否访问指定后台模块/动作。
  • 关键逻辑:
    • 非 defined 类型(如超级管理员)直接放行。
    • 若当前模块为空,拒绝访问。
    • manager 模块的 edit/update 且目标 ID 等于当前管理员 ID 时放行(自编辑)。
    • 子资源 module 名通过别名表归一到父模块后再查白名单。
    • 将 action_list 拆分为数组,判断当前模块是否在列表中。
flowchart TD
S(["canAccess(admin, cur, action, targetId)"]) --> TypeCheck{"type != 'defined' ?"}
TypeCheck --> |是| Allow["允许"]
TypeCheck --> |否| CurEmpty{"cur 是否为空?"}
CurEmpty --> |是| Deny["拒绝"]
CurEmpty --> |否| SelfEdit{"是否 manager 自编辑?"}
SelfEdit --> |是| Allow
SelfEdit --> |否| Alias["子资源归一到父模块"]
Alias --> ListCheck{"cur 是否在 action_list 中?"}
ListCheck --> |是| Allow
ListCheck --> |否| Deny

菜单权限控制(动态菜单与权限过滤)

  • 菜单数据来源:AdminMenuService 提供基础菜单项;ManagerService::moduleOptions 与 adminActionList 结合配置项生成可勾选的模块列表,用于权限分配界面。
  • 权限过滤:后台侧通过 PermissionMiddleware + AdminGate 保证只有具备模块权限的管理员才能访问对应页面;前端侧通过模板或视图层对按钮/链接进行条件渲染(例如仅当拥有某模块权限时才显示对应菜单项或操作按钮)。
  • 使用方式:
    • 新增/编辑管理员时,通过 ManagerService::adminActionList 获取所有可用模块,并根据当前管理员的 action_list 设置勾选状态。
    • 保存时将选中的模块集合写入 action_list 字段,供运行时权限判定使用。

操作权限控制(按钮级与 API 接口级)

  • 按钮级权限:
    • 后台页面中,按钮或操作入口的可见性通常由模板层根据当前管理员的 action_list 进行条件渲染,确保无权限者看不到敏感操作。
    • 即使前端隐藏,仍需以中间件与服务层判定为准,防止绕过。
  • API 接口权限:
    • 通过 api/init/middleware.php 中 auth_modes 声明各模块的鉴权级别(public/optional/required/work_required),UserAuthMiddleware 据此解析登录态并执行工作端身份叠加校验。
    • 未满足鉴权要求时,API 中间件返回 JSON 错误响应(如 401/403)并终止处理。
sequenceDiagram
participant FE as "前端/小程序"
participant UM as "UserAuthMiddleware"
participant CFG as "auth_modes 配置"
participant API as "API 控制器"
FE->>UM : 携带 token 发起 API 请求
UM->>CFG : 读取当前模块鉴权模式
CFG-->>UM : public/optional/required/work_required
UM->>UM : 解析登录态与工作端身份
UM-->>FE : 未通过则返回 401/403
UM-->>API : 通过则执行业务

数据权限控制(基于用户的数据隔离)

  • 思路:在模型层通过 scope 方法注入 user_id 过滤条件,实现“仅能查看/操作本人数据”或“按所属组织/部门隔离”。
  • 示例参考:
    • 会员标签模型提供 scopeFilterByUser,可按 user_id 过滤查询。
    • 聊天配额模型提供 scopeFilterByUserId,可按 user_id 过滤配额记录。
  • 实践建议:
    • 在列表、详情、编辑、删除等所有涉及数据的操作中,统一通过 scope 注入 user_id 条件。
    • 对于跨用户数据访问(如管理员批量操作),需显式校验管理员权限后再放宽过滤。

权限配置的管理界面与使用方法

  • 管理员账号维护:
    • 新增/编辑管理员时,通过 ManagerService::adminActionList 获取模块列表与当前勾选状态,提交后将选中模块拼接为 action_list 字符串存储。
    • 列表展示管理员类型(ALL/ADMIN/DEFINED),便于区分权限范围。
  • 模块选项:
    • ManagerService::moduleOptions 汇总 column_module、single_module 以及基础菜单项,生成可用于筛选与权限分配的模块选项。
  • 操作流程:
    • 进入管理员管理界面 → 选择“自定义权限” → 勾选所需模块 → 保存 → 该管理员登录后仅能访问被授权的模块。

依赖关系分析

  • 中间件依赖:
    • PermissionMiddleware 依赖 AdminGate 进行授权判定。
    • AuthMiddleware 负责会话恢复,为 PermissionMiddleware 提供管理员上下文。
  • 服务依赖:
    • ManagerService 依赖 AdminMenuService 获取基础菜单项,用于权限配置界面。
  • 外部配置:
    • API 鉴权模式由 api/init/middleware.php 集中声明,UserAuthMiddleware 读取并执行。
graph LR
AMW["AuthMiddleware"] --> PMW["PermissionMiddleware"]
PMW --> AG["AdminGate"]
MS["ManagerService"] --> AMS["AdminMenuService"]
UAM["UserAuthMiddleware"] --> CFG["auth_modes 配置"]

性能考虑

  • 中间件链尽量轻量:认证与授权判定应在 HTTP 边界尽早完成,避免不必要的数据库查询。
  • 缓存策略:
    • 管理员 action_list 可在会话或缓存中短期缓存,减少重复解析。
    • 菜单项与模块选项可通过配置与缓存降低渲染开销。
  • 数据权限:
    • 使用 ORM scope 注入 user_id 过滤,让数据库层面完成数据隔离,减少应用层过滤成本。

故障排查指南

  • 无法访问后台模块:
    • 检查管理员类型与 action_list 是否正确配置。
    • 确认当前模块是否存在于 action_list 中;若为子资源,确认已在别名表中登记。
    • 查看 PermissionMiddleware 是否抛出重定向异常。
  • API 接口返回 401/403:
    • 检查 api/init/middleware.php 中对应模块的鉴权模式是否配置正确。
    • 确认请求头携带了有效的 token,且 UserAuthMiddleware 成功解析登录态。
  • 小程序工作台权限失败:
    • 检查 work.permission 接口是否可用,权限校验失败会降级回到首页。

结论

DouPHP 的权限体系以中间件为核心,结合 AdminGate 的授权判定与 ManagerService 的权限配置能力,实现了后台模块访问控制、API 接口鉴权与小程序侧的权限校验。通过数据权限 scope 注入,可实现基于用户的数据隔离。建议在开发中遵循“前端隐藏 + 后端强制校验”的原则,确保权限控制的完整性与安全性。

附录

  • 可参数化中间件接口:
    • ParameterizedMiddleware 定义了 setRouteParameters 方法,支持路由级参数覆盖(如权限节点、限流配额等),使中间件实例化更灵活。
添加日期:2026-10-05