用简单的组织、团队、角色和所有权规则解释多租户 SaaS 权限,附带可扩展的检查清单和示例,帮助构建可预测、安全的访问控制。

权限问题通常从小烦恼开始。有人提交工单:“我是管理员,但看不到发票。”另一个:“为什么我的同事能编辑设置?”人们到处点击、猜测,有时为了图方便会共用一个“所有者”账号,因为这样比整理访问权限要快。
然后各种变通办法堆积起来。团队发明出像 “Admin 2” 或 “Manager (no delete)” 的角色。工程师添加一次性的判断,比如 “如果用户在销售组,就允许导出”,因为它能修复今天的缺陷。一个月后,没人能分辨哪些规则是刻意的,哪些是偶然的。
当你有更多客户时,问题会更严重。对一个公司账户看起来没问题的规则(“管理员可以看到所有数据”)在拥有数百个不同期望的组织时就会崩塌。一个客户想要严格的部门隔离。另一个想要共享工作区。有的只想让合同方访问一个项目而不得见其他数据。如果你的模型不清晰,每个新客户都会变成一个新例外。
目标很简单:可预测的访问规则,能在一分钟内讲清楚。例如:“你的组织拥有数据。团队用于分组。角色定义可执行的操作。资源属于一个组织,有时也属于一个团队。共享遵循少数默认规则。”如果你讲不明白,就很难构建、难以测试,也会让变更变得可怕。
一个值得遵守的承诺是:更少的角色、更清晰的所有权、更安全的默认设置。从与真实岗位对应的一小组角色开始,为每个资源让所有权明显,将默认权限设为最低可用。然后有意地允许共享,而不是意外发生。
如果你的应用服务不止一个客户,在写规则之前先把心智地图画清楚。多租户 SaaS 权限的大多数混淆来自定义义漂移——同一个词在产品不同地方意味着不同事。
为租户边界挑选一个含义并坚持它。很多产品把 “organization” 当作租户:所有数据都在组织内,除非你显式构建共享,否则数据不会跨组织流动。
一个简单且随规模保持清晰的词汇:
“一个人属于多个组织”是常态。顾问可能同时属于三个客户组织,每个组织里的角色不同。因此“用户”和“成员关系”需要分开。大多数权限检查都基于成员关系,而不是用户本身。
团队在能反映真实分组(例如“支持”或“财务”)时很有用。当团队变成第二套权限系统时,它们就是噪音。一个实用的测试是:你是否能用一句话解释该团队的作用而不提到某个具体功能规则。
示例:Maria 用同一个登录切换 Org A 和 Org B。在 Org A 她在财务组并能查看发票;在 Org B 她是查看者只能读取项目。相同用户,不同成员关系,一致的资源类型,边界清晰。
当你把三件事分开时,多租户 SaaS 的权限会更容易理解:
RBAC(基于角色的访问控制)意思是:你把某个角色赋给用户,角色决定允许的操作。角色名称应描述职责,而不是状态。“Billing Admin(计费管理员)”就很清楚。“Power User” 通常会引起争议。
把权限当作动词并在整个产品中保持一致:
然后加入作用域,这样相同的动词就能在不同场景下重复使用。这能避免创建 20 个几乎相同的角色。
常见且易读的作用域有:
如果你发现自己创建了“Project Editor”和“Project Editor (Own)”这类角色,通常是作用域问题,而不是角色问题。
示例:在 CRM 中,让 “Sales Rep(销售代表)” 能创建和编辑交易,但把作用域限制为“仅自有”。让 “Sales Manager(销售经理)” 拥有类似动词但作用域为“团队内”或“全组织”。这样你能少用角色、规则更清晰,当有人换团队时也不容易出现意外。
一个稳妥的默认策略是:角色授予动词,所有权(或指派)限制这些动词生效的范围。
如果你的模型对一个客户有效但在十个客户时就崩了,你很可能把“谁能看”与“谁能做”和“谁是所有者”混在一起了。把它们分开,系统就可预测了。
一个可扩展的规则集:
示例:Sam 属于 Org A 和 Org B。在 Org A,他是成员,能创建和编辑自己的报告,但不能更改计费;在 Org B,他是计费经理,能更新支付方式并下载发票,但除非他的成员关系包括相应区域,否则仍看不到私有项目。
这会让扩展变得“无聊而好”:添加新组织只是增加成员关系和角色,核心规则不变。
写一页纸,让队友两分钟内能读懂。如果你能在不开代码的情况下解释权限,那就说明状态良好。
把要素故意写小一些:
用作用域避免角色爆炸。很多产品只需三种作用域:自有(own)、团队(team)、组织(org)。
| Role | View | Edit | Invite users | Billing | Scope note |
|---|---|---|---|---|---|
| Owner | Yes | Yes | Yes | Yes | Org-wide, can transfer ownership |
| Admin | Yes | Yes | Yes | No/Yes | Org-wide, no ownership changes |
| Member | Yes | Limited | No | No | Own + team (where assigned) |
| Viewer | Yes | No | No | No | Read-only in assigned scope |
自检:把这页给非技术同事看,问一句 “Support 成员能编辑 Sales 报表吗?” 如果他们犹豫,说明你的作用域或团队定义不清。
为了让权限易于理解,先决定谁是每类资源的所有者,然后限制共享选项。
让大部分资源归组织所有。客户通常按公司来思考:发票、项目、联系人、工单和自动化属于组织,不属于某个人。
团队仍然有用,但把团队当作用于路由和可见性默认的工作流标签,而不是隐性的安全逻辑。团队标签可驱动筛选、仪表盘、通知或队列,而访问仍由角色和作用域决定。
用户拥有的资源应为例外,保留给真正个人化的项:草稿、私密笔记、保存的视图、API 令牌或个人设置。当用户离开时,决定处理方式:删除、转移或保留为私有。
一组可读性强的共享规则:
当有人说“我需要访问”时,引导他们选择是哪一级别:他们的个人项、他们团队的工作,还是整个组织。如果不属于这三类,通常说明你的作用域不清晰,而不是需要新共享模式。
示例:一个工单可以归组织所有(便于管理层做全量报表),标签为 Support 团队(进入正确队列),并指派给 Jordan(Jordan 对此负责)。指派不应阻止其他有权查看的角色访问它。
权限常在“人员事件”时出错:邀请、在团队间移动或移除访问。这些流程决定你的模型是否可预测。
把邀请视为创建成员关系的请求,而不是本身就赋予访问。邀请应说明组织、团队(可选)和接受后将获得的角色。
保持规则严格:
临时访问也可在这里处理。不是发明“临时用户”角色,而是允许角色授权带结束日期;到期后访问自动取消,并保留审计记录。
当某人离开组织时,不要自行决定其资源如何处理。如果你的规则是“资源归组织所有”,就坚持它。此人可以保留作为创建者以保留历史,但组织仍是所有者。
如果存在用户拥有的资源,要求在移除前完成敏感项的转移(项目、文档、API 密钥)。
一个登录可以属于多个组织,但应用始终必须有一个“当前组织”。在 UI 明显标示并把所有操作限定到当前组织。
停用通常优于删除。停用能立即移除访问同时保留历史可审计。
大多数权限模型失败是因为规则增长速度赶不上变化。保护好基础(租户边界、所有权、作用域),把其他都当作细节处理。
角色爆炸是经典陷阱。出现边缘情况就创建新角色,而不是用更清晰的权限或作用域解决。几个月后没人知道 “Manager Plus” 到底是什么意思。如果某个特性常被需要,就把它做成一项一级权限;若很少用,用有到期的临时授权处理。
权限漂移更安静却更糟。有人添加了“就这一个例外”然后忘了更新一页纸模型。一年后,书面规则与真实系统不一致。先更新模型,再实现代码。
把团队当作假的安全边界会导致持续混乱。如果资源可以在组织内跨团队共享,要明确说明;如果不能,应该在代码中强制执行,而不是靠名字约定。
早期的危险信号:
如果支持人员需要帮助客户,“给他们全局管理员一会儿”就是租户泄漏的诱因。更好的做法是明确、可记录且范围严格的临时访问(限定一个组织、时间窗和具体动作)。
每个请求应先解析出活动组织(从子域、请求头、会话或路由),若不匹配就拒绝。
在组织上下文之后,把检查按一致顺序执行:先成员关系(他们是否在此组织?)、再角色(在此组织他们被允许做什么?)、最后是所有权或共享(他们是否对这条记录有访问?)。若先做所有权检查,可能会泄露记录是否存在。
用真实账号跑一小组端到端测试,而不是仅靠单元测试:
为改变权限或移动数据的操作添加基本审计事件:角色变更、成员移除、导出、删除、设置更新。第一天不需要完美,但需要能回答“谁在什么时候做了什么?”
检查默认值。新组织和新成员应以最低能成功所需的访问开始。一份面向支持和销售的内部权限 FAQ 也有帮助,包含诸如“团队负责人能否看到其他团队?”和“移除后访问怎么办?”等示例。
从一个小而真实的设置开始:一家公司(一个组织),两个团队——销售和运营。每个人登录一次,然后选择其所属组织。销售需要客户记录和报价,运营需要计费和内部设置。
把团队当作分组和工作流,而不是主要的权限开关。团队可以影响默认值和路由,但不应作为唯一门槛。
选择一小组稳定的角色并在功能上线时保持不变:Admin、Member、Viewer。角色回答“在这个组织你能做什么?”,作用域回答“在哪些对象上能做?”。
增加一条所有权规则:每个资源有组织和所有者(通常是创建者)。编辑在你是管理员,或你是所有者且你的角色包含“编辑自己的”时被允许。查看在你的角色包含该资源类型的“查看”时被允许。
示例:一名销售成员创建了一份报价。另一名销售成员可以查看,但不能编辑,除非该报价被共享给团队或重新指派。运营的查看者只有在规则允许运营查看销售资源时才能看到它。
当你入职 200 个客户组织时,重复使用相同的角色和相同的所有权规则。你只变更成员关系,而不是模型本身。
支持请求如“你能给 X 访问吗?”会变成一个清单:确认组织和资源,检查该用户在该组织的角色,检查所有权和共享,然后更改角色或共享资源。避免一次性例外,并留下审计说明。
把那一页纸模型当作契约。只实现你能在每个 API 调用和每个 UI 屏幕上强制执行的规则,否则权限就会滑向“视情况而定”。
从小处开始:少量角色、清晰的作用域、简单的所有权。当有新需求(“我们能增加一个 Editor-Manager 角色吗?”)时,先考虑收紧所有权或作用域。新角色应当罕见。
为每个新增资源统一基础:
org_id(若适用也存 team_id)在修饰边缘情况前先测试真实流程:邀请、切换组织、管理页以及有人在会话中途失去访问权限时的表现。
如果你用基于聊天的应用构建器开发,先用通俗语言写好权限模型并把它随产品规范放在一起会有帮助。在 Koder.ai (koder.ai) 上,Planning Mode 加上快照与回滚是一种实用方式,用来试验这些场景并确认规则在 Web、后端与移动端表现一致。