告别重复造轮子!ZRAdmin 多租户实战:基于 .NET 8 + SqlSugar 的 SaaS 架构深度解析
做过 SaaS 系统的开发者都知道,多租户是绕不开的"硬骨头"。数据怎么隔离?租户怎么管理?套餐怎么设计?今天带你拆解 ZRAdmin(10K+ Star 的开源后台框架)内置的完整多租户方案,看它如何用 DB-per-tenant + 套餐授权 的组合拳,把这些问题一次性解决。
一、为什么你需要关注这个方案?
先说结论:如果你正在用 .NET 做后台系统,且有多租户/SaaS 需求,ZRAdmin 的多租户方案值得你花 10 分钟读完。
原因有三:
- 开箱即用 ------一个配置项
TenantSettings:UseTenant一键开关,关掉之后系统行为和非多租户模式完全一致,零侵入。 - 物理隔离 ------每个租户独立数据库实例,不是共享表加个
tenantId字段那种"伪隔离",安全性是实打实的。 - 全生命周期覆盖------从租户开通、初始化、停服、续费到注销,套餐管理、菜单授权、用户配额、到期提醒,一条龙。
这不是一个"给你个框架自己搭"的半成品,而是一套经过验证的、可直接投入生产的完整方案。
二、ZRAdmin 是什么?
简单介绍一下背景。ZRAdmin(Admin.Core.ZR)是一款基于 .NET 8 + Vue3/uniapp 前后端分离的通用权限管理后台,Gitee 上 10.7K Star,MIT 开源协议。
技术栈一览:
| 层面 | 技术选型 |
|---|---|
| 后端核心 | .NET 8.0 + Web API + SqlSugar + Swagger |
| 实时通信 | SignalR(在线用户状态管理) |
| 任务调度 | Quartz.NET |
| 缓存 | 内存缓存 + Redis |
| 安全 | 接口限流(IpRateLimit)、数据权限过滤器、SQL 注入防护 |
| 前端 | Vue2.x / Vue3.x / uniapp + Element UI / Ant Design |
| ORM | SqlSugar(支持分库分表、多配置) |
项目地址:https://gitee.com/izory/ZrAdminNetCore
在线体验:http://demo.izhaorui.cn/vue3(账号 admin/123456)
三、多租户架构:为什么选 DB-per-tenant?
多租户隔离方案通常有三种,各有优劣:
┌─────────────────────────────────────────────────────────────┐
│ 多租户隔离方案对比 │
├──────────────┬──────────────┬──────────────┬────────────────┤
│ │ 共享数据库 │ 独立 Schema │ 独立数据库 │
│ │ 共享表 │ (Schema-per) │ (DB-per) │
├──────────────┼──────────────┼──────────────┼────────────────┤
│ 数据隔离性 │ ★★☆☆☆ │ ★★★☆☆ │ ★★★★★ │
│ 实现复杂度 │ ★★★★★ │ ★★★☆☆ │ ★★☆☆☆ │
│ 运维成本 │ ★★☆☆☆ │ ★★★☆☆ │ ★★★★★ │
│ 性能扩展性 │ ★★☆☆☆ │ ★★★☆☆ │ ★★★★★ │
│ 合规性 │ ★★☆☆☆ │ ★★★☆☆ │ ★★★★★ │
└──────────────┴──────────────┴──────────────┴────────────────┘
ZRAdmin 选择了 DB-per-tenant(独立数据库) 方案,核心原因:
1. 数据安全是 SaaS 的生命线
共享表方案中,一个 WHERE tenant_id = ? 漏写就是数据泄露事故。独立数据库从物理层面杜绝了这个问题------租户 A 的代码根本无法访问租户 B 的数据库。
2. 合规需求越来越强
尤其是企业级客户、政府项目,"数据必须物理隔离"几乎是硬性要求。DB-per-tenant 天然满足。
3. SqlSugar 的多配置能力让它变简单
独立数据库方案最大的痛点是"动态路由复杂"。但 SqlSugar 的 SugarScope 提供了多配置能力,可以在运行时动态切换数据库连接,这让 DB-per-tenant 的实现成本大幅降低。
四、架构设计:主库 + 租户库
ZRAdmin 的多租户架构采用 "主库 + 租户库" 的双层数据存储设计:
┌─────────────────────────────────────────────────────────────┐
│ ZRAdmin 多租户架构 │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 主库 (Master DB) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ 租户信息 │ │ 套餐配置 │ │ 平台菜单/权限 │ │ │
│ │ │ SysTenant │ │ Package │ │ (共享数据) │ │ │
│ │ └──────────┘ └──────────┘ └──────────────┘ │ │
│ │ │ │
│ └───────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 租户A 数据库 │ │ 租户B 数据库 │ │ 租户C 数据库 │ │
│ │ │ │ │ │ │ │
│ │ · 用户数据 │ │ · 用户数据 │ │ · 用户数据 │ │
│ │ · 业务数据 │ │ · 业务数据 │ │ · 业务数据 │ │
│ │ · 角色权限 │ │ · 角色权限 │ │ · 角色权限 │ │
│ │ · 操作日志 │ │ · 操作日志 │ │ · 操作日志 │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ 动态路由:SugarScope 根据当前请求的租户ID → 切换到对应数据库 │
└─────────────────────────────────────────────────────────────┘
设计要点:
- 主库存放共享数据:租户信息、套餐配置、平台级菜单和权限
- 租户库存放各自业务数据:用户、角色、业务表、日志等
- 请求进来后,系统从 Token 中解析出租户 ID,通过
SugarScope动态路由到对应数据库
这种设计的好处是:共享数据集中管理不冗余,业务数据物理隔离不串味。
五、核心能力详解
5.1 一键开关:零侵入设计
配置文件中一行搞定:
json
{
"TenantSettings": {
"UseTenant": true
}
}
设为 false 时,整个系统退化为单租户模式,所有行为完全不变。这意味着:
- 你可以在开发阶段关闭多租户,专注业务开发
- 上线时打开开关即可启用多租户能力
- 不需要改任何业务代码
这种"可插拔"的设计思路,非常值得学习。
5.2 租户全生命周期管理
ZRAdmin 把租户的完整生命周期都覆盖了:
创建租户 ──→ 初始化(建库+建表+种子数据)──→ 分配套餐
│ │
│ ┌────────────────────┐ │
│ │ 运行中(正常服务)│←──────────┘
│ │ │ │
│ │ ┌────┴────┐ │
│ │ ▼ ▼ │
│ │ 续费 停服 │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ 恢复服务 重新启用 │
│ └────────┬───────────┘
│ │
└────────────→ 注销(清理数据)
一键开通是最亮眼的功能------点击创建租户,系统自动完成:
- 租户档案录入
- 自动创建数据库实例
- 执行建表脚本
- 写入种子数据(默认管理员、默认角色等)
- 分配默认套餐
- 激活租户
整个过程无需手动建库建表,真正做到了"开箱即用"。
5.3 套餐体系:灵活的商业化基础
预置了 免费版 和 专业版 两套套餐,同时支持自定义套餐的完整 CRUD。
套餐的核心配置维度:
| 维度 | 说明 | 示例 |
|---|---|---|
| 用户配额 | 限制租户最大用户数 | 免费版 10 人,专业版 100 人 |
| 菜单授权 | 控制租户可用的功能模块 | 免费版不含"数据大屏" |
| 到期时间 | 套餐有效期 | 1 个月 / 1 年 |
套餐体系让 SaaS 的商业化变得简单------不同客户买不同套餐,功能自动隔离,无需改代码。
5.4 套餐菜单授权:双重权限防护
这是 ZRAdmin 多租户设计中的一个亮点。
权限计算公式:
实际可用菜单 = 角色菜单 ∩ 套餐菜单
也就是说,即使租户管理员给某个角色分配了某菜单,如果该菜单不在租户购买的套餐范围内,用户依然看不到。
双重防护的意义:
- 第一层:套餐菜单------控制"租户能买什么"(平台级)
- 第二层:角色菜单------控制"租户内谁能用什么"(租户级)
这种设计在商业逻辑和安全逻辑上都是合理的:平台方通过套餐控制售卖范围,租户管理员在套餐范围内做二次分配。
5.5 用户配额自动校验
按套餐限制租户用户数上限。新增用户或批量导入时,系统自动校验:
csharp
// 伪代码示意
if (currentTenant.UserCount >= package.MaxUserCount)
{
throw new BusinessException("当前套餐用户数已达上限,请升级套餐");
}
这个细节很重要------没有配额管理的 SaaS 不是完整的 SaaS。
5.6 到期管理:自动校验 + 主动提醒
- 自动校验:每次请求时检查租户是否过期,过期自动停服
- 到期提醒:支持批量查询即将到期的租户,方便运营团队跟进续费
- 停服恢复:续费后自动恢复服务,数据不丢失
5.7 平台菜单隔离
平台专属功能(如租户管理、全局菜单管理、套餐配置等)仅主租户(平台管理员)可见,普通租户登录后看不到这些入口。这是物理层面之外的第二道防线------就算数据库路由出 bug,前端也不展示,双保险。
六、技术实现揭秘
6.1 动态数据库路由
核心依赖 SqlSugar 的 SugarScope,它支持在运行时动态添加和切换数据库配置。简化后的原理:
csharp
// 1. 定义全局 SqlSugarScope
var sqlSugar = new SqlSugarScope(new ConnectionConfig
{
ConfigId = "master",
ConnectionString = masterDbConn,
DbType = DbType.MySql
});
// 2. 请求进来时,根据租户ID动态切换
public async Task UseTenantDb(long tenantId)
{
var tenant = await GetTenantInfo(tenantId);
// 获取租户数据库连接串
var connStr = tenant.DbConnection;
// 动态添加租户数据库配置(如果尚未添加)
if (!sqlSugar.IsAnyConnection(tenantId.ToString()))
{
sqlSugar.AddConnection(new ConnectionConfig
{
ConfigId = tenantId.ToString(),
ConnectionString = connStr,
DbType = DbType.MySql
});
}
// 切换到租户数据库
sqlSugar.ChangeDatabase(tenantId.ToString());
}
实际实现中会配合 中间件/过滤器 自动完成租户识别和数据库切换,业务代码无感知。
6.2 租户识别流程
HTTP 请求
│
▼
解析 Token → 提取 TenantId
│
▼
查询主库 → 获取租户信息(连接串、状态、套餐)
│
▼
校验租户状态 → 是否停服/过期?
│
▼
切换 SugarScope 到租户库
│
▼
执行业务逻辑(自动路由到租户库)
│
▼
返回响应
整个流程对业务层透明------你的 Service 代码不需要写任何 if (tenantId == xxx) 的判断。
七、实际项目中的应用场景
场景一:企业级 SaaS CRM
一家做销售管理 SaaS 的公司,客户有大型企业和小微企业。
- 大客户要求数据物理隔离 → DB-per-tenant 天然满足
- 小客户预算有限 → 共享服务器但独立数据库,成本可控
- 不同客户买不同功能模块 → 套餐菜单授权自动控制
- 到期续费管理 → 到期提醒 + 自动停服
场景二:教育行业多校管理
一个教育局管理辖区内多所学校,每所学校是独立租户:
- 各校数据完全隔离,互不干扰
- 统一平台管理,一套代码服务所有学校
- 按学校规模分配套餐(用户配额)
- 平台管理员可以看到全局数据,学校管理员只能看本校数据
场景三:ISV 软件服务商
为不同行业客户提供定制化管理系统:
- 每个客户独立数据库,支持独立备份和恢复
- 客户可以随时导出自己的数据
- 新客户开通只需几分钟(一键创建)
- 老客户停用后数据保留,随时可恢复
八、与其他方案对比
| 对比维度 | ZRAdmin 多租户 | 共享表方案 | 自研方案 |
|---|---|---|---|
| 数据隔离 | 物理隔离(独立DB) | 逻辑隔离(tenantId) | 取决于实现 |
| 实现成本 | 开箱即用 | 需要改造每条SQL | 从零开发 |
| 开关控制 | 一行配置 | 需要大改 | 不可逆 |
| 套餐体系 | 内置完整 | 需自研 | 需自研 |
| 用户配额 | 自动校验 | 需自研 | 需自研 |
| 到期管理 | 内置 | 需自研 | 需自研 |
| 维护成本 | 框架升级即可 | SQL遗漏风险高 | 全靠自己 |
| 社区支持 | 10K+ Star 活跃社区 | 无 | 无 |
九、上手指南
快速开始
bash
# 1. 克隆后端代码
git clone https://gitee.com/izory/ZrAdminNetCore.git
# 2. 克隆前端代码(Vue3 推荐)
git clone https://gitee.com/izory/ZRAdmin-vue.git
# 3. 创建数据库,执行初始化脚本
# 4. 修改后端 appsettings.json 中的数据库连接串
# 5. 打开 TenantSettings:UseTenant = true
# 6. 启动后端 + 前端
官方文档:https://www.izhaorui.cn/doc/quickstart.html
体验多租户
- 使用 admin 登录系统
- 进入「租户管理」页面
- 点击「新增租户」,填写租户信息并选择套餐
- 系统自动创建租户数据库并初始化数据
- 切换到租户管理员账号登录,体验隔离效果
十、总结与思考
ZRAdmin 的多租户方案有几个值得学习的设计哲学:
1. 隔离优先,体验统一
选择了 DB-per-tenant 这种"重"方案,但通过 SugarScope 动态路由让业务代码无感知。隔离是物理的,体验是无缝的。
2. 可插拔设计
UseTenant 一个开关,让多租户成为"可选能力"而非"强制架构"。这对于项目前期的灵活性至关重要------你不知道客户最终是否需要多租户,但框架已经准备好了。
3. 商业化思维
套餐、配额、到期管理......这些不是技术功能,而是商业功能。一个好的后台框架不应该只考虑"怎么实现",还要考虑"怎么卖"。ZRAdmin 在这一点上想得很清楚。
4. 双重防护
套餐菜单 ∩ 角色菜单的设计,在安全性和商业逻辑上都站得住脚。这种"防御性设计"的思路值得借鉴。
适合谁用?
- 正在用 .NET 做 SaaS 系统的团队
- 需要多租户能力但不想从零开发的开发者
- 想学习多租户架构设计的 .NET 开发者
- 需要快速搭建企业级后台的项目
项目信息
| 项目 | 地址 |
|---|---|
| 后端源码(Gitee) | https://gitee.com/izory/ZrAdminNetCore |
| 后端源码(GitHub) | https://github.com/izhaorui/Zr.Admin.NET |
| 前端 Vue3 | https://gitee.com/izory/ZRAdmin-vue |
| 官方文档 | https://www.izhaorui.cn/doc |
| 在线体验 | http://demo.izhaorui.cn/vue3 |
| 开源协议 | MIT |
如果这篇文章对你有帮助,欢迎点赞收藏。 项目是开源的,MIT 协议,商用也没问题。觉得好用的话去 Gitee 给个 Star 支持一下作者,开源不易。
你在多租户架构设计中有遇到过什么坑?欢迎评论区交流。



