简介
本文件面向 DouPHP 后台“角色管理”能力,围绕 RBAC(基于角色的访问控制)在系统中的落地进行系统化说明。重点覆盖:
- 角色定义与层次化设计:通过管理员类型与权限集合实现“超级管理员/普通管理员/自定义权限管理员”的层级划分。
- 基础操作:角色创建、修改、删除等流程与校验。
- 权限继承机制:子模块对父模块的鉴权透明继承策略。
- 角色分组与复制:以“动作列表”为粒度进行权限组合与复用。
- 数据模型与权限树:管理员表字段、权限集合与菜单/模块来源。
- 界面与交互:新增/编辑/删除页面、权限勾选、日志审计。
- API 与前端流程:控制器路由到服务层的调用链、表单校验与响应。
- 审计与变更追踪:操作日志记录、过滤与展示。
项目结构
角色管理相关代码主要分布在以下位置:
- 控制器层:负责请求处理、视图渲染与跳转。
- 服务层:封装业务规则、权限判定、数据组装与审计写入。
- 模型层:持久化管理员账号与操作日志。
- 中间件层:身份注入与权限拦截。
- 配置层:模块清单、隐藏项、系统开关等影响权限范围。
graph TB
subgraph "控制器"
MC["ManagerController"]
end
subgraph "服务"
MS["ManagerService"]
AM["AdminGate"]
end
subgraph "模型"
Mgr["Manager"]
Log["ManagerAdminLog"]
end
subgraph "中间件"
AuthMW["AuthMiddleware"]
PermMW["PermissionMiddleware"]
end
subgraph "配置"
CfgMod["module.php"]
CfgSys["system.php"]
CfgApp["config.php"]
end
MC --> MS
MS --> Mgr
MS --> Log
PermMW --> AM
AuthMW --> MC
MS --> CfgMod
MS --> CfgSys
MS --> CfgApp
核心组件
- 管理员控制器(ManagerController)
- 提供列表、新增、编辑、删除、日志等页面入口。
- 仅允许拥有全部权限的管理员创建新管理员;编辑时限制只能编辑本人或具备全部权限者。
- 管理服务(ManagerService)
- 封装新增/更新/删除逻辑、列表与编辑数据构建、权限选项生成、日志查询与渲染。
- 维护“动作列表”的存储格式(逗号分隔),并支持“自定义权限”模式。
- 授权门控(AdminGate)
- 判断当前登录管理员是否可访问指定后台模块/动作。
- 支持子模块对父模块的鉴权别名映射,实现透明继承。
- 管理员模型(Manager)
- 定义 admin 表的填充字段与常用查询方法。
- 中间件(AuthMiddleware、PermissionMiddleware)
- 完成身份注入与权限拦截,将管理员上下文传递给后续控制器与服务。
架构总览
下图展示了从请求进入、身份认证、权限判定到控制器/服务处理的完整链路,以及权限选项与菜单来源。
sequenceDiagram
participant Client as "浏览器"
participant AuthMW as "身份中间件"
participant PermMW as "权限中间件"
participant Ctrl as "管理员控制器"
participant Svc as "管理服务"
participant Gate as "授权门控"
participant DB as "数据库"
Client->>AuthMW : 发起后台请求
AuthMW-->>Client : 未登录则重定向登录
AuthMW->>PermMW : 已登录,传递管理员上下文
PermMW->>Gate : 判定是否可访问当前模块/动作
alt 允许访问
PermMW-->>Ctrl : 放行
Ctrl->>Svc : 执行业务增删改查/日志
Svc->>DB : 读写管理员与日志
Svc-->>Ctrl : 返回结果
Ctrl-->>Client : 渲染页面/跳转
else 拒绝访问
PermMW-->>Client : 返回无权限提示
end
详细组件分析
角色数据模型与权限集合
- 管理员表关键字段
- 用户名、邮箱、密码哈希、动作列表(action_list)、创建时间、最后登录时间与 IP、失败次数与锁定时间、令牌等。
- 动作列表(action_list)
- 存储形式为逗号分隔的模块/动作短名集合。
- 特殊值:ALL(超级管理员)、ADMIN(平台管理员)、DEFINED(自定义权限)。
- 权限来源
- 列式模块(column_module)、单页模块(single_module)、基础菜单(basicMenu)共同构成可选权限项。
- 可通过配置隐藏部分模块或功能。
erDiagram
ADMIN {
int id PK
string username UK
string email
string password
string action_list
datetime created_at
int last_login
string last_ip
int login_fail_count
datetime login_locked_at
string token
datetime token_expires_at
string reset_token
datetime reset_token_expires_at
}
角色创建、修改、删除流程
- 创建角色
- 仅 ALL 权限管理员可创建。
- 支持三种类型:ALL、ADMIN、DEFINED(自定义权限需至少选择一个模块)。
- 密码采用安全哈希存储。
- 成功后写入审计日志。
- 修改角色
- ALL 权限者可编辑任意管理员;非 ALL 权限者仅能编辑自身。
- 修改密码需验证旧密码(兼容历史 MD5 与 bcrypt)。
- 可更新用户名、邮箱、动作列表。
- 成功后写入审计日志。
- 删除角色
- 仅 ALL 权限管理员可删除。
- 禁止删除自己。
- 二次确认后执行删除,并写入审计日志。
flowchart TD
Start(["开始"]) --> CheckAll{"是否ALL权限?"}
CheckAll --> |否| Deny["拒绝: 无权限"]
CheckAll --> |是| Mode{"操作类型"}
Mode --> |创建| Create["校验输入/选择权限/哈希密码/插入记录/写日志"]
Mode --> |更新| Update["校验输入/验证旧密码(如需)/更新字段/写日志"]
Mode --> |删除| Delete["校验目标/禁止自删/二次确认/删除记录/写日志"]
Create --> End(["结束"])
Update --> End
Delete --> End
Deny --> End
权限继承与子模块映射
- 子资源模块(如 AI 任务、AI 日志、微信媒体文章等)在鉴权时透明继承其父模块。
- 当管理员勾选了父模块权限后,对应子资源自动获得访问能力,无需重复配置。
- 新增子资源需在别名表中登记,否则即使父模块已授权也可能被拦截。
flowchart TD
A["当前模块名"] --> B{"是否在子模块别名表?"}
B --> |是| C["映射为父模块名"]
B --> |否| D["保持原模块名"]
C --> E["读取管理员动作列表"]
D --> E
E --> F{"父/原模块是否在动作列表中?"}
F --> |是| G["允许访问"]
F --> |否| H["拒绝访问"]
角色分组管理与角色复制
- 角色分组
- 通过“动作列表”的组合形成不同角色分组(ALL、ADMIN、DEFINED)。
- 自定义权限(DEFINED)可按需勾选模块,形成细粒度分组。
- 角色复制
- 通过复制现有管理员的动作列表,快速创建具有相同权限的新角色。
- 可在新增/编辑时直接沿用已有 action_list 作为模板。
权限树结构与菜单控制
- 权限树来源
- 列式模块(含分类与主表)、单页模块、基础菜单(模块、小程序、主题等)。
- 通过配置项排除隐藏模块,保证权限树与实际可见菜单一致。
- 菜单控制
- 权限中间件依据管理员动作列表决定可访问模块。
- 控制器侧根据权限决定是否渲染“创建”等操作按钮。
graph LR
ModCfg["模块配置<br/>column_module/single_module"] --> Tree["权限树"]
BasicMenu["基础菜单<br/>basicMenu"] --> Tree
HideCfg["隐藏配置<br/>no_show_menu/admin_hidden_single"] --> Tree
Tree --> UI["后台菜单/按钮显示"]
Tree --> Gate["权限判定"]
动态权限检查与菜单控制逻辑
- 动态检查
- 每次请求经权限中间件调用授权门控,结合管理员上下文判定是否放行。
- 针对 manager 模块的“编辑自身资料”单独放行,便于管理员自助维护。
- 菜单控制
- 控制器根据当前管理员的 action_list 决定是否渲染“创建”等入口。
- 列表与编辑页根据权限动态调整可用操作。
sequenceDiagram
participant MW as "权限中间件"
participant Gate as "授权门控"
participant Ctrl as "控制器"
MW->>Gate : canAccess(admin, module, action, targetId)
alt 允许
Gate-->>MW : true
MW-->>Ctrl : 继续处理
Ctrl->>Ctrl : 根据权限渲染按钮/入口
else 拒绝
Gate-->>MW : false
MW-->>MW : 返回无权限
end
角色管理的API接口设计与前端交互流程
- 后端接口(路由到控制器)
- 列表:GET /admin/manager
- 新增:GET /admin/manager/create;POST /admin/manager
- 编辑:GET /admin/manager/edit?id=...;POST /admin/manager/update
- 删除:POST /admin/manager/destroy?id=...(二次确认)
- 日志:GET /admin/manager/log
- 前端交互
- 列表页展示管理员信息、类型、最后登录时间。
- 新增/编辑页提供权限勾选框,按模块/动作维度组织。
- 删除前弹出二次确认,确认后执行删除。
- 日志页支持按用户名、操作类型、模块、IP、日期范围筛选。
sequenceDiagram
participant FE as "前端页面"
participant Ctrl as "管理员控制器"
participant Svc as "管理服务"
participant DB as "数据库"
FE->>Ctrl : GET /admin/manager
Ctrl->>Svc : buildManagerListData()
Svc->>DB : 查询管理员列表
DB-->>Svc : 列表数据
Svc-->>Ctrl : 组装数据
Ctrl-->>FE : 渲染列表
FE->>Ctrl : POST /admin/manager (新增)
Ctrl->>Svc : insert(data)
Svc->>DB : 插入管理员
Svc-->>Ctrl : 返回新ID
Ctrl-->>FE : 重定向至编辑页
FE->>Ctrl : POST /admin/manager/destroy (删除)
Ctrl->>Svc : delete(id, post)
Svc->>DB : 删除管理员
Svc-->>Ctrl : 返回结果
Ctrl-->>FE : 提示成功/失败
角色权限审计与变更追踪
- 审计写入点
- 新增管理员:记录创建操作。
- 更新管理员:记录更新操作(包含旧密码错误场景)。
- 删除管理员:记录删除操作。
- 日志查询
- 支持按管理员账号、操作类型、模块、IP、起止日期筛选。
- 非 ALL 权限管理员仅能查看自身日志,防止越权。
- 日志渲染
- 统一渲染行结构,包含模块、动作、结果、详情、IP、时间等。
flowchart TD
Op["管理员操作"] --> Audit["审计写入"]
Audit --> Store["写入admin_log"]
Store --> Query["日志查询/过滤"]
Query --> Render["渲染日志列表"]
依赖关系分析
- 控制器依赖服务层进行业务处理与数据组装。
- 服务层依赖模型进行数据持久化,依赖配置获取模块清单与系统开关。
- 权限中间件依赖授权门控进行访问控制。
- 审计日志贯穿增删改流程,确保可追溯性。
graph TB
Ctrl["ManagerController"] --> Svc["ManagerService"]
Svc --> ModelMgr["Manager"]
Svc --> ModelLog["ManagerAdminLog"]
PermMW["PermissionMiddleware"] --> Gate["AdminGate"]
Svc --> Cfg["配置(module/system/config)"]
性能考虑
- 权限判定
- 子模块到父模块的映射为常量数组,查找复杂度 O(1)。
- 动作列表解析为字符串分割与 in_array 判断,整体复杂度与权限项数量线性相关。
- 日志查询
- 使用分页与条件过滤,避免全表扫描。
- 非 ALL 权限管理员强制锁定查询范围,减少不必要的数据读取。
- 缓存建议
- 可将模块清单与基础菜单结果进行短期缓存,降低频繁配置读取开销。
故障排查指南
- 无法访问某模块
- 检查管理员类型是否为 defined,且动作列表是否包含该模块或其父模块。
- 若为子模块,确认已在别名表中登记。
- 创建/更新失败
- 检查表单校验与必填项(如自定义权限至少选择一个模块)。
- 更新密码时确认旧密码正确(兼容历史 MD5 与 bcrypt)。
- 删除失败
- 确认不是删除自己;二次确认是否提交。
- 日志为空或受限
- 非 ALL 权限管理员仅能看到自身日志;检查筛选条件是否过严。
结论
DouPHP 后台角色管理以“管理员 + 动作列表”为核心,实现了清晰的 RBAC 分层与灵活的权限组合。通过子模块到父模块的鉴权继承、严格的权限中间件拦截、完善的审计日志与可控的菜单展示,既保证了安全性,又提升了管理效率。建议在扩展新模块时同步更新权限树与别名映射,确保权限体系的一致性。
附录
- 关键路径参考
- 权限判定:AdminGate.php:29-91
- 角色增删改:ManagerController.php:77-175、ManagerService.php:122-243
- 权限选项与菜单:ManagerService.php:548-606
- 日志审计:ManagerService.php:383-467、ManagerService.php:515-540