一个SaaS系统从几十个用户发展到几万、几十万甚至百万用户之后,最容易出问题的地方之一并不是页面,而是数据边界。
用户A登录以后,为什么不能看到用户B的数据?同一个企业里的普通员工为什么不能看到财务部门的资料?企业管理员为什么能够管理本企业员工,却不能管理另外一家公司的员工?而平台自己的超级管理员,又应该拥有多大的权限?
这些问题表面上都是“权限设置”,实际上涉及SaaS系统最基础的数据模型。
设计多用户SaaS系统时,不能只考虑“用户是谁”,还必须同时判断“用户属于哪个组织”“正在访问哪个资源”“这个资源属于谁”“当前角色是否有权限执行这个动作”。
一、先搞清楚SaaS真正需要隔离的是什么
很多初学者设计多用户系统时,会想到给每一个用户建立一个数据库。
例如:
用户A → 数据库A
用户B → 数据库B
用户C → 数据库C
这种方式确实能够形成非常强的物理隔离,但通常不是普通SaaS系统的首选方案。
假设一个系统有100万用户,如果每个用户都有自己的数据库,数据库实例、连接、备份、升级、监控和故障处理都会迅速变得复杂。
更常见的设计是“共享数据库、逻辑隔离”。
例如订单表可以设计成:
orders
其中增加:
tenant_id
user_id
id
amount
created_at
这里的tenant_id就是整个多租户系统非常关键的字段。
它表示这条数据属于哪个租户,也就是哪家公司、哪所学校或者哪个组织。
用户查询数据时,系统不能只写:
SELECT * FROM orders
而应该根据当前登录身份自动加入租户条件,例如:
SELECT * FROM orders WHERE tenant_id = 当前租户ID
这样用户即使知道另外一条订单的ID,也不能仅仅通过修改URL中的ID就拿到其他租户的数据。
二、租户隔离比“用户隔离”更重要
真正成熟的SaaS系统,通常首先建立的是租户,也就是Tenant。
例如一个企业协作系统可能是:
平台
→ 公司A
→ 公司B
→ 公司C
公司A下面又有:
管理员
经理
员工
财务人员
普通成员
因此,权限关系并不是简单的:
管理员 > 普通用户。
而是至少包含三个维度:
用户是谁;
用户属于哪个租户;
用户对这个资源拥有什么操作权限。
例如,公司A的管理员可以管理公司A的员工,但并不意味着他能够管理公司B的员工。
这就是多租户系统和普通“用户登录系统”之间最大的区别之一。
三、不要把“管理员”设计成一个超级密码
很多小型PHP网站最开始的做法是:
普通用户进入前台;
管理员进入后台;
后台管理员拥有全部权限。
这种方式在系统规模较小时还能工作,但一旦进入真正的SaaS环境,就容易出现权限过大的问题。
更合理的方式是采用RBAC,也就是基于角色的访问控制。
例如:
系统超级管理员
租户管理员
部门管理员
财务管理员
普通员工
只读用户
不同角色拥有不同权限。
甚至可以进一步把“角色”和“权限”分开。
角色决定用户属于哪一种身份,而权限决定这个身份可以执行什么操作。
例如:
user.read
user.create
user.update
user.delete
invoice.read
invoice.approve
invoice.export
这样以后增加新的角色,不需要重新编写大量程序,只需要重新组合权限。
四、权限检查必须发生在服务器端
这是很多系统最危险的地方。
前端页面隐藏一个“删除”按钮,并不等于用户没有删除权限。
例如JavaScript把:
“删除用户”
这个按钮隐藏起来。
攻击者完全可以直接向服务器发送:
POST /api/user/delete
如果服务器只检查“用户是否登录”,而没有检查“用户是否拥有删除权限”,那么所谓的前端权限控制实际上没有任何安全价值。
因此真正的权限检查必须发生在后端。
服务器收到请求以后,至少应该确认:
当前用户是谁;
属于哪个租户;
当前角色是什么;
请求访问的资源属于哪个租户;
这个角色是否拥有对应操作权限。
只有全部满足条件,服务器才执行操作。
五、最危险的问题其实是ID越权
假设一个系统有:
/invoice/10001
用户A可以查看10001号发票。
如果用户把地址改成:
/invoice/10002
系统就直接返回另一家公司的发票,这就是典型的对象级授权漏洞,也经常被称为IDOR。
数据库本身可能完全正常。
登录系统也可能完全正常。
问题出在服务器没有检查:
“这张发票是不是属于当前用户或者当前租户?”
所以,任何涉及资源ID的API,都不能只根据ID查询。
应该同时加入所有权或租户条件。
例如:
SELECT * FROM invoices WHERE id = ? AND tenant_id = ?
这一个设计习惯,对于SaaS数据隔离非常重要。
六、管理员数据和普通用户数据不一定要放两个数据库
原稿提出“普通用户独立数据库、管理员独立管理数据库”,这在某些高安全场景确实可以采用,但并不是通用SaaS架构。
管理员本身也是系统中的一种身份。
例如:
users
保存用户身份;
roles
保存角色;
permissions
保存权限;
user_roles
保存用户与角色之间的关系。
业务数据则按照:
tenant_id
进行隔离。
管理员执行操作以后,通过审计系统记录:
谁做的;
什么时候做的;
对哪个租户;
操作了什么对象;
执行了什么动作;
修改前是什么;
修改后是什么。
这样比简单地把“管理员数据”搬到另一个数据库更加完整。
当然,如果平台涉及医疗、金融、政府或者其他具有严格合规要求的数据,可能需要进一步采用独立数据库、独立密钥、独立网络区域甚至独立部署。
隔离等级应该由业务风险决定,而不是为了“看起来安全”就无限增加系统复杂度。
七、平台管理员和企业管理员必须区分
这是SaaS系统经常忽略的一层。
例如一个在线企业管理平台:
平台管理员负责整个SaaS平台。
公司A管理员只负责公司A。
公司B管理员只负责公司B。
这两个角色不能混为一谈。
公司A管理员可能可以:
创建员工;
删除员工;
修改员工权限;
查看公司A的数据。
但他不能:
查看公司B客户名单;
修改公司B员工;
导出整个SaaS平台的数据。
而平台超级管理员可能拥有更高权限,但这种权限也应该受到审计和限制。
这样才能避免一个企业管理员因为程序漏洞获得整个系统的数据访问能力。
八、管理员访问数据并不是“绝对禁止”,而是需要被控制
有些SaaS平台会宣传“管理员绝对无法看到用户数据”。
从技术架构上看,这句话往往需要非常谨慎。
如果平台需要提供故障排查、客户支持、数据恢复或者安全调查,那么某些高权限人员在特定条件下可能需要访问客户数据。
真正成熟的设计不是简单说“管理员永远不能看”,而是建立严格的访问机制。
例如:
普通管理员没有客户数据读取权限;
需要访问时必须提交工单或审批;
敏感操作需要二次认证;
访问行为全部记录;
高风险操作可以要求双人审批;
访问结束后自动失效。
这样管理员权限就从一个长期存在的“万能钥匙”,变成一个受到控制的临时能力。
九、审计日志应该和业务数据分开考虑
管理员修改数据以后,系统不能只留下“updated_at”。
因为:
updated_at = 2026-08-24
只能说明数据什么时候被修改,却不能说明谁修改的、修改了什么。
审计日志可以记录:
actor_id
tenant_id
action
resource_type
resource_id
before
after
ip
user_agent
created_at
例如管理员把某个员工的权限从普通用户改成财务管理员,审计记录应该能够回答:
谁修改的?
为什么有权限修改?
修改的是哪个用户?
修改前是什么权限?
修改后是什么权限?
什么时候修改?
从哪里发起?
对于高价值数据,这种审计能力甚至比单纯的数据库备份更加重要。
十、MFA不是替代权限控制,而是保护高权限账户
多因素认证非常重要,但它解决的是“这个人是不是本人”的问题。
权限控制解决的是“这个人即使是本人,到底允许做什么”。
两者不能混为一谈。
例如一个企业管理员已经通过MFA登录,并不意味着他因此拥有导出所有企业数据的权限。
合理的系统应该是:
身份认证 → 判断用户是谁
租户识别 → 判断属于哪个组织
角色判断 → 判断是什么身份
权限检查 → 判断能执行什么操作
资源授权 → 判断能操作哪一条数据
审计记录 → 保存操作证据
这几层共同构成SaaS的安全边界。
十一、数据库层最好再增加一道防线
如果所有数据隔离都依赖PHP、Node.js或者Java应用代码,一旦程序员某个查询漏写了tenant_id,就可能产生严重的数据泄露。
因此,在安全要求较高的系统中,可以进一步利用数据库本身提供的能力。
例如部分数据库支持Row-Level Security,也就是行级安全策略。
这样即使应用程序执行了一个过于宽泛的查询,数据库层也可以根据当前用户身份限制能够看到的记录。
对于大型SaaS系统,还可以进一步采用独立Schema、独立数据库甚至独立实例。
这些方案的成本和隔离程度不同。
共享表:
成本最低,管理简单。
独立Schema:
隔离程度更高。
独立数据库:
隔离更强,但运维复杂度增加。
独立实例:
隔离等级最高之一,但成本也最高。
没有一种方案适合所有SaaS。
十二、性能设计不能用“缓存权限”解决所有问题
当用户达到百万级以后,权限检查确实可能成为性能问题。
但解决办法不是简单地把所有权限永久放进缓存。
可以把相对稳定的角色和权限关系进行缓存,同时设置合理的失效机制。
例如管理员修改员工权限以后,旧权限缓存必须及时失效。
否则数据库中的权限已经改变,缓存仍然认为这个用户拥有旧权限,就可能形成安全漏洞。
因此权限缓存需要同时考虑:
速度;
一致性;
失效;
权限变更传播。
安全系统里,“快”永远不能成为放弃正确授权的理由。
十三、SaaS最值得采用的基础架构是什么
对于绝大多数中小型SaaS项目,一个比较实用的基础模型可以是:
用户表负责身份。
租户表负责组织。
角色表负责角色。
权限表负责具体能力。
用户角色关系负责授权。
业务表通过tenant_id进行租户隔离。
重要API执行服务器端授权。
管理员操作写入审计日志。
高权限账户启用MFA。
数据库备份与审计日志独立管理。
随着业务规模和安全要求增加,再逐步升级到独立Schema、独立数据库或者独立实例。
这种设计比一开始就给每一个用户建立一个数据库更加容易维护。
十四、真正需要防范的是“跨租户访问”
SaaS安全设计最后可以归结成一个非常简单的问题:
“当前这个人,为什么能够看到这条数据?”
如果程序无法明确回答这个问题,权限体系就还不够成熟。
用户登录,只能证明身份。
管理员身份,也不能自动证明对所有资源拥有无限权限。
一个请求从进入服务器开始,就应该经过身份认证、租户识别、角色判断、资源授权和审计记录。
而数据库中的每一条重要业务数据,都应该能够明确回答“它属于哪个租户”。
SaaS系统最大的安全风险往往不是数据库服务器被黑客直接攻破,而是一个看似普通的API因为漏掉了一项授权检查,让公司A的用户能够读取公司B的数据。对于多租户系统来说,最重要的安全边界不是“管理员页面和普通用户页面长得不一样”,而是从数据库、API到权限模型,每一层都必须知道数据属于谁、谁可以访问,以及为什么可以访问。
当系统规模扩大以后,再根据业务风险决定是否采用独立数据库、独立部署、专用密钥等更高级的隔离方式。这样既能控制安全风险,也不会因为一开始的过度设计,把一个本来简单的SaaS系统变成难以维护的数据库工程。
SaaS 多用户系统怎么设计,普通用户和管理员的数据应该如何区分
喜欢这篇报道?
使用下面的功能,方便以后继续阅读和分享 MNewsTV
关于文章收藏
收藏不需要注册帐号,收藏信息仅保存在当前浏览器中。
删除收藏请进入「我的收藏」进行管理。
分享 Facebook | X | WhatsApp | LinkedIn
捐助(Paypal): https://www.paypal.me/observeccp 订阅中国观察电报 Telegram : https://t.me/s/ObserveCCP