Spring Boot多租户数据隔离有数据库级、Schema级和表级三种方式,需依安全与扩展性权衡选型;动态数据源适配AbstractRoutingDataSource,结合TenantContext与请求上下文识别租户;表级隔离须强制tenant_id过滤并禁用原生SQL;异步场景需透传租户标识,全链路须贯穿租户上下文。

在 Spring Boot 中实现多租户 SaaS 系统的数据隔离,核心是让不同租户的数据在逻辑或物理层面互不可见。常用方式有三种:数据库级隔离(每个租户独立库)、Schema 级隔离(同库不同 schema)、表级隔离(同库同 schema,靠字段区分)。实际选型需权衡安全性、运维成本和扩展性。
基于动态数据源的数据库/Schema 级隔离
适合对数据安全要求高、租户数量中等(几百以内)的场景。通过 AbstractRoutingDataSource 动态切换数据源或 schema,配合请求上下文(如 Header、子域名)识别租户。
- 定义 TenantContext 工具类,用 ThreadLocal 存储当前租户标识(如 tenantId 或 schemaName)
- 继承 AbstractRoutingDataSource,重写 determineCurrentLookupKey(),返回当前租户对应的数据源 key 或 schema 名
- 在 WebFilter 或 HandlerInterceptor 中解析租户信息(例如从 Host: app1.example.com 提取 app1),并设置到 TenantContext
- MyBatis 可结合 @SelectProvider 或拦截器自动注入 schema(如 PostgreSQL 的 SET search_path TO tenant_a);JPA 需配合 Hibernate 的 MultiTenantConnectionProvider 和 CurrentTenantIdentifierResolver
基于租户字段的表级逻辑隔离(Shared Database, Shared Schema)
租户共用同一套表结构,在每张业务表增加 tenant_id 字段,并确保所有查询自动带上该条件。简单轻量,但强依赖开发规范,容易因漏加条件导致数据越界。
- 使用 JPA 的 @Filter + @FilterDef 定义全局过滤器(如 @Filter(name = "tenantFilter", condition = "tenant_id = :tenantId")),并在实体类上启用 @FilterJoinTable
- MyBatis Plus 可通过 MetaObjectHandler 自动填充 tenant_id,并结合 Mybatis-Plus 的自动分页插件 + 拦截器(InnerInterceptor)统一追加 WHERE tenant_id = ? 条件
- 务必禁用原生 SQL 直接操作(如 @Select("SELECT * FROM order")),改用 Wrapper 或 Criteria API;所有手动 SQL 必须显式包含 AND tenant_id = #{tenantId}
租户识别与上下文传递的关键细节
租户识别不能只靠登录态,因为一个用户可能属于多个租户(如管理员跨租户管理),必须在每次请求中明确携带租户上下文。
将后端 Java 接口文档转换为前端 TypeScript 类型定义(interface / type)。输入:飞书文档、Spring Boot 接口文档、Swagger/OpenAPI、手写 API 文档等。输出:包含请求参数与响应结果的 .md 文件,含完整 JSDoc 注释。触发关键词:Java 转 TS、接口转换、生成前端类型、接口文档转 TypeScript、DTO/VO/BO 转 interface、后端文档转前端类型、生成接口类型、类型定义生成。
- 推荐方式:通过 HTTP Header(如 X-Tenant-ID)、子域名(tenant1.api.example.com)或请求路径前缀(/t/{tenantId}/orders)提取租户标识
- 避免仅依赖 JWT 中的 tenant_id,除非该 token 是按租户单独签发且无法跨租户复用
- 异步任务(@Async、RabbitMQ 消费者、定时任务)中 ThreadLocal 会丢失,需显式透传 tenantId(如封装在 Message 中,或使用 TransmittableThreadLocal)
- 数据库连接池(如 HikariCP)要支持租户粒度的配置(如最大连接数、超时时间可差异化)
配套保障措施不能少
光靠技术隔离不够,还需机制兜底。
- 所有 DDL 脚本需支持动态 tenant_id 或 schema 占位符(如 Flyway 的 placeholderReplacement + ${tenant_schema})
- 日志记录必须包含 tenant_id,便于问题追溯;审计日志需强制记录操作租户上下文
- 单元测试要覆盖多租户场景,比如模拟不同 tenant_id 查询,验证结果无交叉
- 提供租户级数据导出/备份能力,避免 DBA 误操作影响全量租户
不复杂但容易忽略:租户隔离不是一劳永逸的配置,而是贯穿数据访问全链路的设计约束。从 Controller 入参校验、Service 事务边界、Mapper 查询逻辑,到后台任务和定时同步,每一层都要主动感知并应用租户上下文。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










