当前位置: 首页 > news >正文

多租户 SaaS 权限怎么设计?从组织、角色到资源隔离

很多 SaaS 系统一开始只有个人用户。

数据库表里有一个user_id,查询数据时加上:

WHERE user_id = ?

基本就能满足需求。

但当系统开始支持企业版、团队协作、成员管理后,权限模型就会变复杂。

例如:

一个企业有多个成员 不同成员权限不同 管理员可以管理组织 普通成员只能查看自己的内容 某些记录属于整个团队 某些数据只属于个人

这时如果继续只靠user_id判断,很快就会出现权限混乱。

多租户 SaaS 权限设计的核心,是先区分几个概念:

用户是谁 他属于哪个租户 他在租户里是什么角色 他能访问哪些资源

一、什么是多租户?

多租户可以简单理解为:一套系统服务多个企业或组织,但不同组织之间的数据必须隔离。

例如:

A 公司使用同一个 SaaS 系统 B 公司也使用同一个 SaaS 系统

它们可以共用同一套代码、同一套服务,甚至同一个数据库集群。

但 A 公司不能看到 B 公司的数据。

因此,系统中通常会引入一个核心字段:

tenant_id

也可以叫:

organization_id team_id workspace_id company_id

具体命名不重要,重要的是它代表“数据属于哪个组织”。


二、基础表结构怎么设计?

一个简单的多租户模型可以包含三张核心表。

用户表:

CREATE TABLE user ( id BIGINT PRIMARY KEY, email VARCHAR(128) NOT NULL, name VARCHAR(64), created_at DATETIME NOT NULL );

租户表:

CREATE TABLE tenant ( id BIGINT PRIMARY KEY, name VARCHAR(128) NOT NULL, plan_type VARCHAR(32), created_at DATETIME NOT NULL );

成员关系表:

CREATE TABLE tenant_member ( id BIGINT PRIMARY KEY, tenant_id BIGINT NOT NULL, user_id BIGINT NOT NULL, role VARCHAR(32) NOT NULL, status VARCHAR(32) NOT NULL, created_at DATETIME NOT NULL, UNIQUE KEY uk_tenant_user (tenant_id, user_id) );

这里不要把tenant_id直接写死在用户表里。

因为一个用户未来可能属于多个组织。

例如:

用户自己有个人账号 同时是 A 公司成员 又被邀请进 B 公司项目

用成员关系表会更灵活。


三、角色怎么设计?

最常见的是 RBAC,也就是基于角色的访问控制。

基础角色可以设计为:

OWNER:组织拥有者 ADMIN:管理员 MEMBER:普通成员 VIEWER:只读成员

不同角色拥有不同能力:

权限动作OWNERADMINMEMBERVIEWER
管理成员
修改套餐
创建任务
查看团队记录按规则按规则
删除组织

一开始不建议把角色设计得太细。

如果上来就有十几个角色,后期维护会很麻烦。

更推荐先用少量稳定角色覆盖主要场景,再在复杂业务中补充具体权限点。


四、权限判断不要只看角色

很多系统会写成:

if (member.getRole().equals("ADMIN")) { allow(); }

这样虽然简单,但不够灵活。

更好的方式是判断某个用户是否拥有某个动作权限。

例如:

permissionService.check( userId, tenantId, "record:delete" );

权限点可以设计成:

member:invite member:remove record:view record:delete task:create billing:manage setting:update

这样业务代码不需要关心用户是管理员还是拥有者,只需要关心他能不能执行当前动作。

角色和权限点之间的对应关系,可以放在配置中。


五、数据隔离必须放在后端

前端可以隐藏按钮,但不能作为权限安全边界。

例如普通成员不应该看到“删除团队记录”按钮。

但即使按钮被隐藏,用户仍然可能通过接口请求:

DELETE /api/records/10001

所以后端必须检查:

当前用户是否属于该租户 当前资源是否属于该租户 当前用户是否有删除权限

例如查询记录时:

SELECT * FROM translation_record WHERE id = ? AND tenant_id = ?;

不要只写:

WHERE id = ?

否则只要猜到资源 ID,就可能越权访问其他组织的数据。


六、资源表都要带 tenant_id 吗?

多数业务表都建议带上tenant_id

例如:

CREATE TABLE translation_record ( id BIGINT PRIMARY KEY, tenant_id BIGINT NOT NULL, created_by BIGINT NOT NULL, title VARCHAR(128), created_at DATETIME NOT NULL );

这样做有几个好处:

查询时可以直接按租户过滤 更容易建立联合索引 方便做数据归档和迁移 权限判断更明确

常见索引可以这样建:

CREATE INDEX idx_tenant_created ON translation_record(tenant_id, created_at);

这样查询某个组织下的记录列表时,性能会更稳定。


七、个人资源和团队资源要分清

SaaS 产品里经常同时存在两类资源:

个人资源 团队资源

例如同言翻译(Transync AI)这类实时翻译产品,用户可能既有个人翻译记录,也可能在企业组织下产生会议记录、翻译任务、会议总结和成员使用数据。

这时就要明确:

个人记录:只有创建者能看 团队记录:组织内有权限的人能看 企业使用明细:管理员或拥有者能看

可以在资源表中增加:

owner_type owner_id tenant_id created_by

例如:

owner_type = PERSONAL owner_id = user_id owner_type = TENANT owner_id = tenant_id

这样系统可以同时支持个人版和企业版,而不是为两套业务复制两套表。


八、邀请成员也要考虑状态

成员邀请通常不只是插入一条成员记录。

更完整的状态包括:

INVITED:已邀请,未接受 ACTIVE:已加入 REMOVED:已移除 EXPIRED:邀请过期

成员表可以增加status字段。

用户真正登录并接受邀请后,再变成ACTIVE

权限判断时,只允许ACTIVE成员访问组织资源。

if (!member.getStatus().equals("ACTIVE")) { throw new ForbiddenException(); }

这样可以避免“邀请了但未加入”的账号提前拥有权限。


九、权限系统要记录操作日志

企业版功能通常需要审计。

例如:

谁邀请了成员 谁删除了记录 谁修改了组织设置 谁查看了使用明细 谁调整了套餐或席位

可以建立操作日志表:

CREATE TABLE audit_log ( id BIGINT PRIMARY KEY, tenant_id BIGINT NOT NULL, operator_id BIGINT NOT NULL, action VARCHAR(64) NOT NULL, target_type VARCHAR(64), target_id BIGINT, created_at DATETIME NOT NULL );

操作日志的价值不只是在出问题时追责,也能帮助管理员理解团队内部发生了什么。


十、多租户权限检查清单

设计 SaaS 权限时,可以检查:

1. 是否区分 user_id 和 tenant_id? 2. 一个用户是否可以加入多个组织? 3. 成员关系是否有状态? 4. 是否定义了清晰的角色? 5. 角色是否映射到具体权限点? 6. 后端是否校验资源所属租户? 7. 业务表是否包含 tenant_id? 8. 个人资源和团队资源是否分开建模? 9. 管理员和普通成员的数据范围是否不同? 10. 是否记录关键操作日志?

总结

多租户 SaaS 权限设计,不能只靠一个user_id字段解决。

更稳妥的模型通常包括:

用户表 租户表 成员关系表 角色与权限点 资源 tenant_id 隔离 个人资源与团队资源区分 操作审计日志

对于个人版产品来说,权限模型可以简单一些。

但一旦产品进入企业版、团队协作、组织管理阶段,就应该尽早建立多租户模型。

否则后期再补tenant_id、角色、成员状态和资源隔离,改造成本会非常高。

http://www.jsqmd.com/news/1108528/

相关文章:

  • 实战案例:基于AI视觉与视频分析的SOP装配动作实时防错系统落地分享
  • 5秒解锁音乐自由:ncmdump如何让网易云NCM格式秒变通用MP3
  • 3个设计哲学:芋道源码如何重塑企业级Java开发范式
  • Sunshine游戏串流:打造你的终极跨平台游戏云主机
  • 从公开资料看必火AI数字人:产品定位、核心场景与内容链路
  • 3大AI图像处理工具,让Krita选区效率提升10倍
  • 房产估价模型:三层可解释架构与领域特征工程实战
  • Agent记忆管理怎么设计的?
  • 手机号查QQ号终极指南:本地化隐私保护解决方案
  • FreeClip2 | 请叫我小HiFi!新手闭眼抄作业✅
  • Windows本地语音识别终极指南:3大核心技术突破让电脑自动记录一切对话
  • 芋道源码框架深度解析:5大企业级架构优势与技术选型指南
  • paperxie 论文写作实操指南:跟着页面填写流程,一站式搞定全类型学术文稿
  • 大模型学习路线图:小白也能轻松入门,收藏这份进阶指南
  • Sunshine游戏串流主机完全指南:如何快速搭建你的跨平台游戏云
  • VMware虚拟机网络不通?7个致命配置陷阱正在偷走你的连接(附逐行诊断脚本)
  • RustNfsSvc - Windows下的 NFS4 服务器
  • 深度解析UnrealPakViewer:Pak文件分析的3大核心技术实现
  • Range-based 循环语法
  • 收藏!没有CS背景?小白也能逆袭成为AI工程师(附实操路线图)
  • 【计算机毕业设计案例】基于 SpringBoot 的应急仓储物资数字化管理系统的设计与实现 基于 SpringBoot 的应急物资申领出库审批系统(程序+文档+讲解+定制)
  • stl 容器新增的实用方法介绍
  • 如何免费解锁Wand专业版:从2小时限制到无限游戏体验
  • Selenium WebDriver在.NET 4.8.1 ClickOnce部署中的五大痛点与解决方案
  • 移动云盘签到 - 青龙面板自动签到脚本
  • STM32与74HC32硬件消抖键盘设计实践
  • 程序员量化交易实战 28:把价格输入抽象成价格源
  • auto 关键字
  • 千屏千面,第一眼不能砸:登录场景的“适配“与“防闪“之道
  • 工业瑕疵检测项目启动要多久?