权限系统设计:ADMIN、EDITOR、VIEWER、USER 如何分工
本文目录
角色回答的是“这类身份通常可以做什么”,不是“这个人永远能读取任意对象”。可见菜单、前端路由守卫和按钮禁用只改善体验;每个 API 仍要在服务端检查主体、动作、目标资源、租户/所有权和当前状态。
先定义动作与资源
列出 document、category、media、book、comment、site-config、user、audit 等资源,再细分 list/read/create/update/publish/delete/restore/purge/upload/export/manage-role。资源类型不能替代对象范围:EDITOR 有编辑文档的能力,不代表可以编辑所有其他用户的私密草稿。
| 角色 | 典型能力 | 明确禁止或额外条件 |
|---|---|---|
| ADMIN | 全站配置、账号和角色管理、危险操作 | 高风险操作要二次确认并审计;生产密钥仍不应通过界面明文暴露 |
| EDITOR | 创建/编辑、提交审核、处理授权范围内的内容 | 角色管理、密钥、永久清除等能力单独限制 |
| VIEWER | 查看已授权的后台结构与内容 | 所有写、导出敏感数据、草稿和隐藏资源访问均拒绝 |
| USER | 个人资料、阅读进度、评论及已授权读物 | 不能访问他人私有对象或后台管理接口 |
矩阵是起点,不是代码中的唯一授权逻辑。可以用 RBAC 表达宽泛能力;作者归属、书籍访问级别、发布时间、账号冻结、环境和租户范围通常要加 ABAC/关系判断。把所有例外塞进角色名会变成角色爆炸。
服务端决策顺序
- 校验 session/token 签名、过期、发行方和受众;再从可信数据源检查账号启用状态和角色版本。
- 默认拒绝没有明确规则的动作。新增 API 路由时必须有授权测试,不继承“同模块其他路由都公开”的隐式假设。
- 先限定查询范围,再访问对象;不要先按任意 ID 读出全文,最后只在 UI 隐藏。
- 变更权限、下架用户或撤销角色时定义旧会话失效机制。长寿命 JWT 通常需要短 TTL、角色版本/撤销列表或服务端 session 才能快速撤权。
- 对隐藏资源按风险选择 403 或 404;错误不得包含资源正文、内部路径或确认敏感对象是否存在的信息。
测试矩阵
为每个角色和动作做正向与负向用例:VIEWER 对新增、编辑、发布、删除、恢复、历史回滚都返回拒绝;USER 猜测他人的对象 id 不能横向越权;EDITOR 不能改系统密钥;已冻结账号的旧 token 不能继续读取。API 直连测试比“按钮没显示”更能证明权限实现。
还要覆盖批量接口、搜索、导出、文件下载、历史版本、缓存和 WebSocket/SSE 订阅。检查对象列表总数、分页游标和错误消息,避免通过数量差异或缓存读到未授权数据。授权日志记录规则版本和拒绝类别,不记录密码、token、正文全文。
把授权决策放在可测试的策略层,输入为服务端可信主体、动作、目标对象和必要上下文;查询时将 tenant/owner 条件下推到数据库,避免读取任意对象后才在内存中过滤。列出每个 API 的 public、authenticated、role、object-level 等级;新路由没有明确分类就不应默认公开。撤权时还要清理进程缓存、下载签名和长连接订阅。
权限策略应能在本地用纯数据测试,不依赖真实账号或生产数据库。每次新增角色先检查它与现有角色的能力差异;若只差一两个特殊动作,优先用显式权限或资源关系表达,避免复制出 ADMIN_LITE、EDITOR_PLUS 等无法治理的角色。批量操作按每个对象重新授权,不能因为请求中的第一个对象通过就放行整批。
参考:OWASP Authorization Cheat Sheet 与 Authorization Patterns。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。