5.4 权限管理(RBAC)
概述
RBAC(基于角色的访问控制)链路:user →(user_role)→ role →(role_menu)→ menu(type=1 权限节点)。身份在 JWT 中间件加载(缓存 300s),权限判定在 Bind 包装层(HasPerm),前端按通配权限放开按钮。
RBAC 授权链路图
text
请求 ──▶ JWT中间件 ──▶ LoadIdentity(userID)
│
user ─────────┤ dept_id → DeptId(数据权限用)
user_role ────┤ role.code → Roles[]
│ role.data_scope → DataScope(多角色取最宽松)
role_menu ────┤
menu(type=1, ┤ permission → Perms[]
status=0, │
hide=0) ────┘
▼
缓存 easygoadmin:user:identity:{uid} JSON TTL 300s
▼
Bind 包装层 HasPerm("sys:user:page"):
permission=="" → 通过 │ Roles 含 ROLE_SUPER_ADMIN → 通过
│ Perms 精确匹配 → 通过 │ 否则 403
数据范围经 ctx(WithDataScope/WithDeptID)随行,在 applyFilters 末环过滤
缓存失效:角色保存权限/改数据范围 / 用户分配角色/改部门 等主动 Del,过期兜底身份加载(LoadIdentity,缓存 300s)
text
1) user 行回查 dept_id → DeptId(单列查询)
2) user_role → roleIds → role.code 集合(Roles)
同一查询顺带读 role.data_scope:多角色取最宽松(数值 min,
1 最宽 4 最严;缺列值按"全部"参与比较)→ DataScope
3) role_menu → menuIds → menu 中 type=1 且 status=0 且 hide=0 的
permission 非空去重集合(Perms)
4) 超管/平台:DataScope 强制"全部"(过滤层另有 dataScopeBypass 双保险)权限判定(consts.HasPerm)
text
判定顺序:
1. permission 标签为空 → 通过(仅登录即可,如 /api/index/*)
2. Roles 含 ROLE_SUPER_ADMIN → 通过(超管全放行)
3. Perms 精确匹配 → 通过
4. 否则 → code=403 信封
权限码规范:sys:{模块小驼峰}:{action}
前端识别超管通配权限 *:*:*(getUser 返回)放开全部按钮权限。权限点声明与管理
| 环节 | 实现 |
|---|---|
| 声明 | 接口 gmeta permission:"sys:user:page" 标签 |
| 存储 | goframe_menu 表 type=1 行(permission 列) |
| 创建 | 生成器自动创建(7 固定 + status/import/export 按需)或菜单管理手工 |
| 分配 | 角色管理勾选 → POST /api/role/menu/save 重写 role_menu + 清身份缓存 |
| 判定 | Bind 包装层 HasPerm(每请求) |
角色管理扩展接口:
text
GET /api/role/menu/list/:roleId 查询角色已勾选菜单(sys:role:getPermission)
POST /api/role/menu/save 重写 role_menu + 清理相关用户身份缓存
(sys:role:savePermission)菜单树:GetUserMenuTree 由 user→role→role_menu→menu 组装(index.GetMenus 消费);超管返回全量菜单。
缓存失效链
text
身份缓存 easygoadmin:user:identity:{uid} 删除时机:
* role.SavePermission → clearRoleIdentityCache(roleId)
→ 查该角色全部 user → 逐个 Del
* role.Update(含 data_scope 变更)→ AfterUpdate → 同上
* user.AssignRoles(user.Update/Add 均经此,改部门亦然)
→ clearIdentityCache(userId)
* 兜底:300s 自然过期前端配合
- 权限指令(directives/permission.ts)按 getUser 返回的 permissions 控制按钮显隐
- 超管返回
*:*:*,指令识别通配直接放行 - 后端不做按钮级校验——按钮级是前端 UX 控制,接口级是后端强制(新增接口必须声明 permission 或评审确认留空)
安全注意
text
* hide=1 的权限节点不进 Perms——"禁用权限"用 status,不能用 hide
* 权限留空 = 登录即可,评审需明确该选择(新接口默认应声明权限)
* 身份缓存 300s 窗口内直接改库不会即时生效(API 路径均主动清缓存)
* 超管绕过全部权限校验——平台管理端点的收紧依赖节点设计