前三层管什么
后台的可见性与可操作性,实际由四层共同决定。前三层是常规权限模型:
| 层 | 判断依据 | 典型问题 |
|---|---|---|
| 1 功能权限 | 角色-菜单 / 角色-权限项 | 这个运营能不能看结算单 |
| 2 模块开关 | 已购模块白名单 | 客户买的是基础版,不该出现旗舰版模块 |
| 3 平台专属 | 平台身份(PlatformOnly) | 租户超管不该看到厂商控制面页面 |
这三层都基于"这个人",而第四层基于"这套系统当前的状态"。
第四层:授权状态
授权过期或进入受限模式时,规则和角色无关:
- 所有写操作禁止(下单、发布、结算、创建);
- 只保留登录、查看、续费相关页面;
- 顶部常驻状态条,说明原因与到期日。
问题是:前三层都在"是否给权限"这一维度上工作,第四层是"整体降级"。如果直接复用角色权限,会出现两个坑:
- 受限时把角色权限清空,恢复授权后原权限无法还原;
- 每个接口都写一段
if (restricted)判断,漏一处就是一个洞。
所以第四层做成独立的裁剪层:不改角色数据,而是在"装配菜单"和"校验接口"两个环节各插一次。
实现:菜单白名单 + 接口守卫
菜单装配:登录后返回的菜单树 = 角色可见菜单 ∩ 模块白名单 ∩ 平台边界 ∩ 受限白名单。四层取交集,顺序固定,任何一层拒绝即不可见。
接口守卫:在鉴权中间件之后再加一道授权中间件,按"当前授权状态 + 请求方法"判断。受限模式下,非白名单内的写请求统一返回明确错误码(不是 403 也不是 500,而是"授权受限"专用码),前端据此弹续费提示,而不是"操作失败"。
一个细节:读与写分开判断。受限模式允许读,否则客户连"看看到期前还有多少数据"都做不到,体验会非常糟。
定时任务也要受限
容易漏的是后台任务:授权过期了,但账期扫描、静态页生成、短信发送仍在跑。这类任务在受限模式下应该暂停写操作类的,只保留不影响业务的(如日志清理)。
做法是执行器在取任务前检查授权状态,按任务类型决定是否跳过,并在日志里写明"因授权受限跳过"。否则客户会看到"任务执行成功但什么都没发生"。
为什么用白名单而不是黑名单
受限模式只允许少量页面(登录、概览、授权信息、续费引导),所以白名单更安全:新增功能默认不在白名单内,不会因为"忘了加黑名单"而在受限状态下被放行。
这与我们整体的权限设计原则一致:默认拒绝,显式放行。
一条经验
授权与权限是两套语义:权限回答"谁可以做什么",授权回答"这套系统现在有什么能力"。把它们混在一张表里,一定会在"续费后权限没回来""受限时页面还能进"这类问题上反复返工。
分开之后,好处是状态可解释:客户问"为什么我不能下单",后台能直接显示"授权已过期,写操作已暂停",而不是让人去翻角色配置。