一个员工能不能同时加入两家公司?一个顾问能不能同时管理十几个客户?一个会计师能不能进入不同企业的财务系统?对于现代SaaS来说,答案通常是可以。
问题一旦从“一个用户属于一个企业”变成“一个用户可以属于多个企业”,传统的账户设计马上就会遇到麻烦。因为这时候用户的身份和企业之间已经不是简单的一对多关系,而是一个典型的多对多关系。
这也是多组织SaaS设计中最容易被低估的地方。
最基本的模型应该把“用户”和“组织”分开。
用户代表的是登录身份。例如一个人拥有一个邮箱和一个密码,可以使用同一个账户登录SaaS系统。组织则代表企业、学校、团队、机构或者其他业务边界。
真正把两者连接起来的,是一张关系表。
可以把它理解成:
users保存用户身份;
organizations保存企业或者组织;
memberships保存“谁属于哪个组织,以及在这个组织里是什么身份”。
例如,用户张三可以同时出现在组织A和组织B的membership记录中。在组织A里,他可能是管理员;在组织B里,他可能只是普通成员。
这时候最关键的一点出现了:角色不能简单地放在users表里。
如果users表只有一个role字段,那么张三在组织A是管理员、在组织B是普通员工这种情况就无法表达。
因此,更合理的结构通常类似于:
users
id
email
password_hash
organizations
id
name
memberships
user_id
organization_id
role_id
status
created_at
如果一个用户可以在同一个组织中拥有多个角色,还可以进一步把角色关系独立出来。
这样设计之后,“用户是谁”和“用户在某个组织里拥有什么权限”就被分开了。
这是多组织SaaS非常重要的架构原则。
身份是全局的,权限通常是组织范围内的。
例如张三登录系统以后,系统首先知道“这是用户123”。但是当他打开公司A的财务报表时,系统还必须知道“他现在是在组织A的上下文中操作,而且他在组织A拥有财务读取权限”。
如果随后他切换到公司B,权限也必须随之改变。
这就是为什么多组织系统通常需要一个明确的“当前组织”概念。
用户登录以后,可以拥有多个membership,但一次请求通常应该明确属于哪个组织上下文。例如URL、session、token或者请求头中包含当前组织标识。服务器随后必须重新验证:这个用户是否确实属于这个组织,以及他在这个组织中的角色和权限是什么。
千万不能因为用户拥有组织A的权限,就默认他也可以访问组织B。
数据隔离是整个系统最重要的一道防线。
假设数据库里有一张invoices表:
id
organization_id
customer_id
amount
created_at
当张三正在操作组织A时,查询发票不能只写:
SELECT * FROM invoices;
也不能只根据用户ID判断。
系统必须确保查询范围属于当前授权的organization_id。
更重要的是,这个限制不能只依靠前端。
如果网页上有一个“切换企业”的下拉菜单,用户可以看到组织A和组织B,并不意味着后端可以相信浏览器传过来的organization_id。攻击者完全可以手工修改请求,把organization_id从A改成B。
因此,后端必须重新验证用户与组织之间的membership关系,然后再执行数据查询。
这也是多租户SaaS最危险的漏洞之一:越权访问其他租户的数据。
一个简单的WHERE organization_id = ?看起来很普通,却可能成为整个系统的安全边界。
更成熟的系统还会把tenant context或者organization context放进请求处理链中。请求进入服务器以后,先确定用户身份,再确定当前组织,再确定用户在该组织中的权限,最后业务代码才能访问组织数据。
这样做的好处是,可以尽量避免每个业务开发人员都重新发明一套权限判断逻辑。
数据库本身也可以参与安全隔离。
最简单的是共享数据库、共享表,每张业务表增加organization_id。这种方式成本低,适合大量中小型组织,是很多SaaS系统实际采用的方案。
例如:
orders
organization_id
id
customer_id
amount
customers
organization_id
id
name
projects
organization_id
id
name
这样同一套数据库结构就可以服务成千上万家企业。
但这里有一个非常重要的工程问题:索引设计。
如果一张表包含数千万甚至数亿条数据,仅仅给id建立索引并不一定够。大量查询都是“某个组织下的某些记录”,因此经常需要结合organization_id设计复合索引。
例如:
INDEX (organization_id, created_at)
对于某些查询,则可能需要:
INDEX (organization_id, status, created_at)
这样数据库可以更有效地缩小搜索范围。
当企业规模非常大、监管要求非常严格,或者不同客户之间存在明显的数据隔离要求时,也可以考虑更强的隔离方式,例如独立数据库、独立schema,甚至专属基础设施。
但这并不是“数据库越分越安全”。
数据库越分,运维、备份、迁移、升级、监控和跨租户统计都会变得更加复杂。因此,多租户架构通常是在安全、成本和运营复杂度之间寻找合理的平衡,而不是追求一种绝对正确的数据库结构。
缓存也必须遵守同样的原则。
假设Redis中保存:
user:123
通常问题不大。
但如果保存的是组织相关数据,就必须把组织边界考虑进去。例如:
organization:456
而不是简单使用:
dashboard
否则用户A刚刚读取的企业数据,有可能因为缓存key设计不当,被用户B读取出来。
这类错误往往不是数据库SQL写错,而是缓存架构把原本正确的数据隔离重新打破了。
权限设计则是另外一层。
RBAC,也就是基于角色的访问控制,非常适合多数SaaS系统。
例如组织管理员可以管理成员,财务人员可以读取和编辑账单,普通员工只能查看自己的项目。
但实际企业往往比这复杂。
同一个“项目经理”可能只能管理自己部门的项目;财务经理可以查看整个企业的账单;外部顾问可能只能访问被邀请的几个项目。
这时候单纯的角色已经不够,需要进一步结合资源、部门、项目或者用户属性进行判断。
ABAC,也就是基于属性的访问控制,可以处理这种更细粒度的规则。
不过,也没有必要为了显得高级,就把所有权限系统都设计成复杂的ABAC。很多SaaS系统完全可以从组织级RBAC开始,在真正出现复杂授权需求以后,再增加资源级权限。
一个成熟的权限系统通常至少应该区分三个层次。
第一是平台级权限。
例如SaaS运营人员是否可以管理所有组织、处理平台配置或者查看系统级监控数据。
第二是组织级权限。
例如某个用户是不是组织管理员,能不能邀请成员、修改组织设置或者查看企业账单。
第三是资源级权限。
例如某个用户能不能查看项目A、编辑订单B或者批准某一笔付款。
这三层如果混在一起,系统很快就会出现“管理员到底是谁”“这个权限是全局的还是企业内部的”之类的问题。
安全设计还有一个常见错误,就是把密码当成普通敏感数据进行AES加密。
密码通常不应该以可逆方式保存。正确做法是使用专门的密码哈希算法,例如Argon2id或者bcrypt,并使用成熟的密码验证接口进行校验。
因为系统根本不需要知道用户原来的密码是什么,只需要知道输入的密码是否与保存的哈希匹配。
至于企业数据本身,则可能需要根据具体敏感程度采用加密存储、传输加密、密钥管理以及访问控制等措施。
审计日志同样非常重要。
多组织系统发生安全事件以后,管理员需要回答的不只是“谁登录过”,而是“谁在什么时候,以什么身份,在哪个组织里,对什么资源进行了什么操作”。
因此审计记录通常至少应该能够关联用户、组织、资源、操作类型、时间以及必要的请求信息。
例如:
user_id = 123
organization_id = 456
action = invoice.update
resource_id = 789
timestamp = ...
这样当企业发现一笔异常操作时,管理员才有能力追踪整个过程。
多组织架构还有一个经常被忽视的问题:用户离开企业以后怎么办?
假设张三同时属于公司A和公司B。公司A将他移除之后,他当然应该立即失去公司A的数据访问权限,但不能因此删除他的整个users账户,因为他仍然属于公司B。
所以membership通常应该拥有自己的status,例如active、suspended或者removed。
这也是为什么不能简单地把“用户删除”理解成“用户退出某家公司”。
对于企业来说,成员关系本身就是一项独立的数据。
邀请机制也应该围绕membership设计。
企业A邀请一个邮箱加入系统时,系统可以创建一个邀请记录。用户接受以后,再建立user与organization之间的membership关系。
如果这个邮箱已经拥有SaaS账户,就不需要创建第二个用户。
他只需要增加一个新的membership。
这就是多组织账户最大的用户体验优势:一个人可以使用一个身份进入多个企业,而不需要为了每一家企业重新注册一个邮箱和密码。
当然,企业级SaaS还可能进一步出现组织层级。
例如:
集团公司
子公司A
子公司B
子公司C
这时简单的organization_id可能还不够,需要设计organization hierarchy,或者使用parent_organization_id等结构。
集团管理员可能可以查看子公司的汇总数据,而子公司的普通管理员不能反向看到集团其他公司的数据。
这实际上已经从“多组织”进一步进入了“组织层级权限”问题。
所以,一个用户能不能加入多个企业?
不仅可以,而且对于很多现代SaaS产品来说,这是非常自然的设计。
真正需要认真设计的不是“能加入几个企业”,而是加入之后每个企业的身份是否独立、权限是否独立、数据是否严格隔离,以及用户退出一个企业之后,其他企业的账户是否还能正常使用。
一个可靠的多组织SaaS架构,可以把整个问题浓缩成一句话:用户身份是一个东西,组织成员关系是另一个东西,组织权限又是另一个东西,业务数据必须明确属于某个组织。
一旦这几个概念在数据库和代码层面被清楚地分开,系统以后增加团队、部门、项目、子公司、合作伙伴甚至集团架构时,才有足够的扩展空间。
反过来,如果一开始就在users表里塞一个organization_id,然后所有业务表都默认相信这个字段,那么系统也许能很快上线,但等到第一个客户要求“一名员工同时管理三家公司”时,开发团队往往才会发现,真正需要重构的不是一个页面,而是整个身份、权限和数据模型。
多组织SaaS最值得投入的地方,从来不是把权限按钮做得多复杂,而是把“谁是谁、他现在代表谁、他能看什么、这些数据属于谁”这四个问题,在系统底层定义得足够清楚。这样企业数量增加以后,系统仍然可以保持安全、可维护和可扩展。
一个用户可以加入多个企业吗?SaaS 多组织账户应该如何设计
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
分享 Facebook | X | WhatsApp | LinkedIn
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP