一、核心逻辑:什么是"双层隔离体系"?
JeecgBoot 的多租户设计并非简单的"一行数据一个租户",而是采用了平台层 (System) 与 租户层 (Tenant) 分离的双层架构。这是保证 SaaS 平台安全与灵活性的基石。
二、 层级对比表
| 特性 | 平台层 (System) | 租户层 (Tenant) |
|---|---|---|
| 代表账号 | admin (超级管理员) |
tenant_admin, user_a 等 |
租户 ID (tenant_id) |
0 (或 null/特殊标识) |
1, 2, 3... (具体业务租户ID) |
| 权限范围 | 全局管理 创建租户、分配套餐、系统级配置、监控所有租户状态。 | 租户内管理 仅管理本部门/本租户的用户、角色及业务数据。 |
| 数据可见性 | 跨租户可见 可查看所有租户列表及统计信息,但不直接介入具体业务数据隔离(除非特殊授权)。 | 严格隔离 SQL 自动注入 tenant_id = ?,绝对无法看到其他租户数据。 |
| 定位 | 系统的"上帝视角" | 业务的"独立王国" |
三、核心结论
admin 账号绝不属于任何普通租户!
它的逻辑设计是独立的平台层 。如果将 admin 设置为 tenant_id = 1,它将失去管理其他租户的能力,导致 SaaS 平台架构崩塌。
这种设计实现了"行政与业务分离"。平台管理员负责"建房子"(创建租户),租户管理员负责"住房子"(业务运营)。
如果强行设置:如果你把 admin 的 tenant_id 改成了 1(某个具体租户):
这就是刚开启Saas多租户时候的坑,admin账户默认有一个租户,导致了:
- 权限降级:
admin将被限制在租户1的隔离范围内,可能无法再访问"租户管理"菜单去创建或管理其他租户。 - 逻辑冲突: 系统代码中通常有判断逻辑,如果
tenant_id为 0,则跳过某些租户拦截器,赋予全局权限。 - **数据视野受限:**他将无法查看全局的系统监控、所有租户列表等跨租户功能。
四、如何管理租户
既然 admin 不在租户内,它是如何操作租户数据的?流程如下:
- 1.登录:使用
admin/password登录。此时系统识别其为平台管理员(无具体租户或租户ID=0) - 2.创建租户:进入【系统管理】->【租户管理】,点击"新增"。
- 3.分配管理员:
- 方式 A(推荐):在创建租户时,直接指定一个租户管理员账号(例如
zhangsan)。这个zhangsan用户的tenant_id会被设为100,并授予"租户管理员"角色。 - 方式 B:先创建租户,再去【用户管理】新建一个用户,将其关联到该租户,并赋予角色。
- 方式 A(推荐):在创建租户时,直接指定一个租户管理员账号(例如
- 4.切换视角:
admin始终拥有上帝视角。
如果需要测试租户 100 的功能,不要修改 admin 的属性,而是登录 zhangsan (租户管理员) 的账号,或者使用系统的"切换租户/模拟登录"功能(如果有)。
五、常见误区排查
-
误区:"我是
admin,为什么我看不到租户 A 的具体业务数据(如订单)?"- 正解:这是正常的!多租户的核心就是隔离。
admin只能管理租户的元数据(创建、删除、延期),不能直接越权查看租户的业务数据。如果需要查看,通常需要通过"数据权限"特殊配置,或者登录该租户的管理员账号查看。
- 正解:这是正常的!多租户的核心就是隔离。
-
误区:"我想让
admin也能作为租户 A 的老板。"- 正解:最佳实践是一人一号。
admin负责平台运维;租户 A 的老板应该拥有一个独立的账号(如boss_a),该账号属于租户 A。如果必须用同一个账号,JeecgBoot 部分版本支持"切换租户"功能,即一个用户关联多个租户,登录时选择或点击右上角切换,但admin这种系统内置账号通常不建议混用。
- 正解:最佳实践是一人一号。
| 误区现象 | 原因分析 | 解决方案 |
|---|---|---|
| admin 登录后看不到某些租户数据 | 拦截器配置错误,未将 0 或 admin 排除在过滤规则之外。 |
检查 TenantLineInnerInterceptor 的逻辑,确保 tenant_id=0 时跳过过滤。 |
| 普通用户能查到其他租户数据 | 自定义 SQL 未走拦截器,或手动拼接 SQL 漏了租户条件。 | 尽量使用 MP 的 Wrapper;自定义 XML SQL 需手动添加 ${ew.customSqlSegment} 并确保实体类有租户字段。 |
| 新创建的租户无法登录 | 创建租户后未初始化默认角色或菜单关联。 | 检查"创建租户"的业务逻辑,确保执行了 initTenantData() 方法。 |
| 数据库报错:Column 'tenant_id' not found | 某些系统表(如字典表、日志表)不需要多租户隔离,但未配置忽略表。 | 在拦截器配置中,将 sys_log, sys_dict 等表加入 ignoreTable 列表。 |
| 前端传参 tenant-id 一直为 0 | 前端未正确获取或传递租户ID,或者后端未从 Token 中解析。 | 检查前端请求头设置,确认后端 SecurityUtils.getUser() 能正确拿到租户信息。 |