能力接入

租户、操作身份与上下文

区分身份、目标租户、实体归属与数据范围,核对读写隔离。

浏览全部手册
本页目录
文档来源与 Markdown 原文

权威正文:ineed-core/docs/guides/tenant-and-context.md。网站按工作区快照同步,原文中的历史日期和验证范围保留。

开始编码前核对同版本源码;跨仓文件引用可在源码定位目录查找。

下载 Markdown 原文 ↓

规则 CORE-TENANT-001。新增实体、自定义查询、关系写入、后台任务、缓存均需检查本规则。

先区分四个概念

  • 操作身份:谁执行操作,由受信任会话/任务身份建立。
  • 目标租户:本次选择在哪个租户范围工作。
  • 实体租户:数据实际归属。
  • 数据范围:租户内部还允许访问哪些数据,由业务与授权模块进一步约束。

租户过滤不能代替 API 授权,角色授权也不能替代数据行的租户校验。客户端传入一个 tenantKey 不会自动成为可信操作上下文。

实体模式

来源:TenantScopeMode

模式 数据语义 写入时重点
TENANT_ONLY 当前租户数据 不能保存成全局租户行
GLOBAL_ONLY 全局数据 行必须属于全局租户,要求具备全局维护身份
GLOBAL_WITH_TENANT 全局数据与当前租户数据可一起读取 普通租户能读取全局行不代表能修改它
TENANT_SELF 租户主体自身 创建/修改规则不同于普通租户业务实体,查看具体实现

标准 ID 读取使用 findEntityById / findAllEntityById / existsEntityById。JDBC、JPA、MyBatis-Flex、MongoDB 的这些 Core 能力在返回实体前通过 EntityTenantSupport.isReadable 过滤租户范围;同步 FindByIdManager 转换 VO 前再做相同检查。单条不可见时 Manager 返回 DATA_NOT_FOUND,批量结果排除不可见行,存在性检查返回 false。非租户实体不受此过滤;TENANT_SELF 沿用租户主体可跨目标读取的语义,API 授权仍由业务约束。

这是 Core 能力入口的返回值隔离,不是数据库级行安全。底层原生 findById / findAllById / existsById / selectOneById、自定义 SQL、派生方法及 join 不自动获得这些检查;业务应使用标准能力或显式加入读取范围。R2DBC 的标准仓储基类在查询条件中限定范围。任何数据栈都不能把这些租户规则当作逻辑删除、字段脱敏或租户内数据范围授权。

写入链路

EntityTenantSupport分别处理初始化、创建和修改校验。init 在缺少实体租户时按模式和目标租户补值;多租户下缺少必要目标会报错。

checkModifyAccess(Object) 先检查操作身份与实体模式,再检查当前目标租户。其带显式操作租户参数的重载只做对应写权限判断,不能当作包含全部目标租户检查的通用替代。

对于非全局行,修改时目标租户必须与实体租户相符;支持全局行的模式另有分支。即使操作身份可维护多个租户,也应在选定目标范围内操作。单租户模式下还会考虑配置租户的维护资格,具体见上述源码。

标准账户管理动作(重置密码、启停、密码过期标记、解锁时间)会先读取账户,再检查操作身份与目标租户,缺失或越界时不发出更新。响应式检查在订阅链内获取上下文。底层认证计数操作不属于该管理入口,认证模块需维护自己的可信调用边界。

新建和修改应沿 Core 标准生命周期执行;业务直接调用仓储时必须自行确认绕过了哪些校验。优先通过已有 Manager 能力组合,而不是在每个入口复制租户判断。

同步与响应式上下文

同步入口使用 ContextHandler。响应式入口使用 ReactiveContextHandler,上下文必须随订阅链传播。

不要在 Publisher 装配时提前读取当前用户,也不要把同步 ThreadLocal 当作任意异步线程都能读取的事实。后台任务需要显式建立并清理受信任上下文,不能沿用上次请求残留。WebFlux 请求语言保存在 OperateInfo 的 Reactor Context 中,使用 ReactiveContextHandler.getLocale() / ReactiveI18nHandler 读取;过滤器不向共享线程写入 LocaleContextHolder

验证清单

准备租户 A/B 和全局数据,验证创建、查询、按 ID 读取、修改、状态、删除、关系绑定及自定义查询。至少包含:A 修改 B 失败、全局身份选 A 不能修改 B、普通身份不能修改全局行、支持全局读取时返回集合正确、缺失上下文不越界。

源码级例子:EntityTenantSupportTest。它证明租户策略,不替代各数据栈真实 SQL 和 HTTP 授权测试。

ID 读取与账户写入的实际 JDBC 验证见 ID 仓储集成测试账户隔离测试,使用 H2;其他阻塞适配器另有默认方法契约测试,不代表已连接其生产数据库。

仍有疑问?按反馈清单整理复现信息 →