RBAC 权限系统方案 — 最小改动版
现状分析
当前系统的权限控制非常简单:
| 角色 | 值 | 状态 |
|---|---|---|
| Guest | 0 | 占位,未使用 |
| Common | 1 | 普通用户 |
| Admin | 10 | 管理员 |
| custom | 11 | 已定义,未实现 |
| Root | 100 | 超级管理员 |
权限判断只有一种方式:role >= 阈值。没有细粒度控制——要么看到整个管理区,要么完全看不到。
最小改动方案:复用 options 表 + 内存缓存
核心思路
在现有 options 表中新增一个 key RolePermissions,存 JSON 格式的角色权限矩阵。不建新表、不改用户表结构。
完全复用 SidebarModulesAdmin 的模式:options 表存储 → OptionMap 内存缓存 → /api/status 下发给前端。
存储格式
{
"10": ["menu:admin.channel", "menu:admin.models", "channel:create", "channel:update", "..."],
"11": ["menu:admin.redemption", "menu:admin.user", "redemption:create", "..."],
"1": ["menu:chat.playground", "menu:console.token", "token:create", "..."],
"100": ["*"]
}
- key 是角色值的字符串,value 是该角色拥有的权限数组
- Root 用
["*"]代表全部权限 - Guest 无条目
权限命名规则:资源:动作
菜单权限(控制页面/菜单可见性):
menu:chat.playground menu:chat.chat
menu:console.detail menu:console.model_usage menu:console.token
menu:console.log menu:console.midjourney menu:console.task
menu:personal.topup menu:personal.personal
menu:admin.channel menu:admin.models menu:admin.deployment
menu:admin.subscription menu:admin.redemption menu:admin.user
menu:admin.setting操作权限(控制页面内按钮/动作):
channel:create channel:update channel:delete channel:test
token:create token:update token:delete
user:create user:update user:delete user:set_role
redemption:create redemption:update redemption:delete
model:create model:update model:delete
setting:update log:delete各角色默认权限矩阵
| 权限类别 | Common(1) | Admin(10) | Sale(11) | Root(100) |
|---|---|---|---|---|
| menu:chat.* / console.* / personal.* | 全部 | 全部 | 全部 | * |
| menu:admin.channel/models/deployment/subscription | - | 有 | - | * |
| menu:admin.redemption/user | - | 有 | 有 | * |
| menu:admin.setting | - | - | - | * |
| channel 增删改测 | - | 全部 | - | * |
| token 增删改 | 全部 | 全部 | 全部 | * |
| user 增删改 | - | 有(无 set_role) | - | * |
| redemption 增删改 | - | 全部 | 全部 | * |
| setting:update / log:delete | - | log:delete | - | * |
改动清单
后端(4 个文件新增,3 个文件修改)
| 操作 | 文件 | 说明 |
|---|---|---|
| 新增 | common/permissions.go |
权限常量定义 + DefaultRolePermissions() 返回默认矩阵 + 从 OptionMap 解析的内存缓存 HasPermission(role, perm) |
| 修改 | model/option.go |
InitOptionMap 加入 RolePermissions 默认值;updateOptionMap 中加 case 解析 JSON 到内存缓存 |
| 新增 | middleware/permission.go |
RequirePermission(perms...) 中间件,从缓存查权限,无权返回 403 |
| 新增 | controller/role_permission.go |
Root-only API:查询/更新/重置角色权限(读写 options 表的 RolePermissions key) |
| 修改 | router/api-router.go |
各路由组追加 RequirePermission;兑换码路由从 AdminAuth 改 UserAuth + RequirePermission(激活 Sale);加权限管理路由 |
| 修改 | controller/user.go |
GetSelf/setupLogin 返回 permissions 字段(从缓存读当前角色权限列表) |
| 修改 | controller/misc.go |
GetStatus 返回 RolePermissions(供前端全局使用) |
前端(3 个文件新增,5+ 个文件修改)
| 操作 | 文件 | 说明 |
|---|---|---|
| 新增 | helpers/permission.js |
hasPermission(perm) / hasAnyPermission(...perms) 工具函数,从 localStorage 读权限 |
| 新增 | hooks/common/usePermission.js |
React Hook 封装 |
| 新增 | pages/Setting/System/RolePermissionSettings.jsx |
Root 权限管理 UI:按角色 Tab + 按分类分组的权限开关 |
| 修改 | helpers/auth.jsx |
新增 PermissionRoute 组件(按权限控制路由访问) |
| 修改 | App.jsx |
Admin 路由从 <AdminRoute> 改为 <PermissionRoute permission="menu:admin.xxx"> |
| 修改 | hooks/common/useSidebar.js |
三层可见性加入权限层:管理员全局配置 AND 角色权限 AND 用户偏好 |
| 修改 | components/layout/SiderBar.jsx |
isAdmin() → hasAnyPermission('menu:admin.*') |
| 修改 | 各 Actions 组件 | 按钮根据操作权限条件渲染(如 hasPermission('channel:delete') 才显示删除按钮) |
i18n(7 个文件修改)
所有 web/src/i18n/locales/*.json 添加权限名称和权限管理页面的翻译。
数据流
启动 → InitOptionMap 写入默认 RolePermissions → 内存缓存就绪
↓
用户登录 → setupLogin → 从缓存读角色权限 → 返回 { permissions: [...] }
↓
前端存 localStorage → useSidebar/usePermission 读取 → 菜单和按钮按权限显示
↓
API 请求 → UserAuth/AdminAuth(第一道门槛)→ RequirePermission(第二道门槛)
↓
Root 修改权限 → UpdateOption("RolePermissions", json) → 刷新缓存
→ 用户下次刷新页面自动获取新权限向后兼容
- 不改 users 表,
role字段不变 - 保留
UserAuth/AdminAuth/RootAuth作为第一道门槛 RequirePermission作为叠加的第二道门槛- 前端检测不到
permissions时回退到原有isAdmin()/isRoot()逻辑 - 首次启动自动写入默认权限,现有用户无感迁移
总结
- 不新增数据库表,复用 options 表存一条 JSON
- 不改用户表结构
- 后端新增 3 个文件、修改 4 个文件
- 前端新增 3 个文件、修改 5-8 个文件
- 完全兼容现有逻辑,渐进式叠加