工程工具知识花园
多空间权限:交集,不是并集
结论先行: 多空间应用的权限不能只看"用户是谁",必须先看"请求在哪个空间"。完整检查顺序固定为六步:解析空间、固定归属、计算组织动作、叠加项目角色求交集、提交时快照归属、服务端最终裁决。前端按钮的状态只是体验提示,服务端门禁、资源归属快照、跨空间数据隔离才是正式安全边界。
第一步:先解析请求属于哪个空间
权限计算的第一步不是查用户身份,而是解析请求的作用域:这个请求属于个人空间,还是某个团队空间。
- 请求必须显式声明空间;旧值只按兼容规则映射,非法值直接拒绝。
- 缺失时只能按明确的默认规则选择,不能脑补。
- 作用域、组织、项目、系列是四个不同层级的对象,不能用一个组织 ID 或一个全局角色替代。
把四个层级混成一层,是多空间越权最常见的起点。
第二步:归属一旦固定,请求体改写不了
- 个人空间的组织 ID 为空;团队空间绑定组织。
- 不能用请求体里的组织 ID 去覆盖已经解析出的作用域。
- 个人用户没加入团队,照样可以用个人空间;平台管理员也不能凭平台身份自动读取团队资源。
归属是解析出来的事实,不是客户端声称的事实。
第三步:能力是交集,只会缩小,不会放大
- 管理员、创建者、审核者、只读成员各自只有明确的模块动作集合,多一个动作都没有。
- 项目或系列的负责人、编辑、审核者、只读者,再与组织动作求交集:组织层面能生成,不代表在当前项目里能编辑。
- 只读成员可以查看允许的数据,但不能通过直达链接、键盘入口、GET 副作用、缓存键或异步任务绕过写入门禁。
交集是设计的本意:组织角色回答"能做什么动作",项目角色回答"在哪个项目里",两个答案取交之后,才是这个人在这一次请求里的真实能力。验收时要准备双组织测试数据:同一个人在两个组织里的动作集合可能完全不同,只测一个组织等于没测。
第四步:异步任务按提交时快照,不随空间切换改写
任务、生成记录和推送,保存的是提交那一刻的空间与组织快照。用户之后切换空间,已提交的任务归属不变,也不允许把任务跨空间查询。
没有快照的异步任务,等于把归属的决定权交给了"查询的那一刻",而那一刻的空间上下文可能是错的。
第五步:正式边界在服务端,不在界面
- 页面隐藏入口或置灰按钮,只是提示。直接调接口、媒体、任务、下载、导出,同样要过服务端门禁。
- 资源不存在时的 403 与 404 口径遵循接口契约,但响应、文件字节和缓存都不能泄露其他空间的资源。
- 浏览器刷新、前进后退、双账号同源会话、旧链接,必须纳入验收。只测"按钮是不是灰的",等于没测。
验收有一套固定顺序:先准备双组织测试数据,再按接口、页面、缓存、下载、导出的顺序逐个验收,最后补上浏览器刷新和双账号场景。顺序不能乱:接口门禁没过,页面按钮测得再漂亮也没用。
一个真实教训
一个视频工作台项目里曾经出现过一次越权:历史数据的组织 ID 为空,按兼容规则回退之后,团队空间看到了别人的项目。根因正是归属和解析混在了一层——旧数据没有空间快照,兼容映射又给了它一个合法身份。修复方向和上面的检查顺序完全一致:解析、固定归属、求交集、服务端裁决,一步都不能省。
小结
多空间权限就三句话:先看请求在哪,再看人是谁,最后取交集。界面的状态是做给人看的,服务端的门禁才是做给绕路的人看的。