Repository navigation
feat(mybatis): fill audit fields and scope statements by tenant from the operator context - #69
Merged
Merged
Conversation
…the operator context
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
Audit fields and tenant isolation are needed by most business systems and are usually wired by hand: a thread-local somewhere, a
MetaObjectHandlerthat reads it, a tenant plugin that reads something else. The roadmap asks for these MyBatis-Plus features to be driven by one operator context. The risk worth designing around is multi-tenancy: a statement that runs without a tenant condition reads every tenant's data, and nothing fails.Changes
ddk-core:Operator(id, name, tenant) andOperatorContext.runAs/callAsrestore the previous operator when they finish, so a pooled thread does not carry one request's operator into the next;wrapcarries the operator to another thread.OperatorResolverbean turns a request into anOperator. When it is present, a filter sets the context for the request and clears it afterwards. DDK does not assume how authentication works.MetaObjectHandleralso fillscreateByandupdateBy(StringorLong) from the operator; without an operator they stay empty;ddk.mybatis.tenant.enabled=trueadds MyBatis-Plus'sTenantLineInnerInterceptor, first in the chain, with the tenant taken from the operator.flowchart LR R[HTTP request] --> F[OperatorContextFilter<br/>OperatorResolver → Operator] J[Job / listener] -->|OperatorContext.runAs| C F --> C[(OperatorContext)] C --> A[AuditMetaObjectHandler<br/>createBy / updateBy] C --> T[TenantLineInnerInterceptor<br/>WHERE tenant_id = ?] A --> SQL[SQL] T --> SQLMulti-tenancy fails closed:
Not included: data permissions, which depend on each application's organization model. Soft delete is left to MyBatis-Plus's own
@TableLogic; DDK adds nothing on top of it, and this PR does not test the combination with the generic repository.背景
审计字段和租户隔离是大多数业务系统都需要的能力,通常靠手工拼接:某处放一个 ThreadLocal,一个
MetaObjectHandler去读它,租户插件又读另一个来源。路线图要求这些 MyBatis-Plus 能力由同一个操作者上下文驱动。设计时最需要防的是多租户的风险:一条没带租户条件的语句会读到所有租户的数据,而且不会有任何报错。改动
ddk-core:新增Operator(标识、名称、租户)与OperatorContext。runAs/callAs结束时恢复之前的操作者,线程池里的线程不会把上一个请求的操作者带给下一个请求;wrap用来把操作者带到另一个线程。OperatorResolverBean,把请求解析成Operator。有这个 Bean 时,过滤器在请求期间设置上下文、结束时清除。DDK 不假设认证方式。MetaObjectHandler同时填充createBy、updateBy(String或Long),取自操作者;没有操作者时保持为空;ddk.mybatis.tenant.enabled=true时加入 MyBatis-Plus 的TenantLineInnerInterceptor,排在插件链最前面,租户取自操作者。数据流见上文流程图。多租户取不到租户时拒绝执行而不是放行,两种豁免都必须显式声明,见上文伪代码。
未包含:数据权限,它取决于各个应用自己的组织模型。逻辑删除交给 MyBatis-Plus 自带的
@TableLogic,DDK 没有在它之上再做什么;它与通用仓储的配合本 PR 没有测试。