关键在于构建泛型上下文类tenantdatascope封装多维隔离参数,配合策略化mybatis拦截器动态注入sql过滤条件,并通过注解或配置实现运行时策略装配与安全兜底。

用泛型类配合拦截器实现多维度动态数据隔离,关键在于把“租户”“部门”“角色”“用户”等不同维度的过滤逻辑从硬编码中解耦出来,让它们可配置、可组合、可复用。这不是加一个tenant_id = ?就能解决的事,而是要构建一套运行时可插拔的数据访问守门员体系。
泛型数据上下文类:统一承载多维隔离参数
定义一个泛型上下文类,比如TenantDataScope<t></t>,其中T代表隔离策略类型(如TenantScope、DeptScope、UserScope)。这个类不直接操作数据库,只负责携带当前请求所需的所有隔离元信息:
- 封装
tenantId、deptIds(支持树形结构ID列表)、userId、roleCodes等字段 - 提供
isScopeEnabled()和getFilterCondition()方法,由具体策略子类实现 - 通过
ThreadLocal<tenantdatascope>></tenantdatascope>在请求链路中透传,避免Controller/Service层感知细节
策略化拦截器:按需加载不同维度的SQL改写逻辑
MyBatis拦截器不再只做“加tenant_id”,而是根据上下文中的策略类型,动态选择执行哪套过滤规则:
- 拦截
Executor.update和Executor.query方法,在SQL解析前读取TenantDataScope实例 - 若为
DeptScope,则注入dept_id IN (?, ?, ?)或递归查询子部门的CTE语句 - 若为
UserScope,自动追加create_by = ?或owner_id IN (SELECT user_id FROM user_dept WHERE dept_id IN (...)) - 支持策略叠加,例如同时启用租户+部门,生成
WHERE tenant_id = ? AND dept_id IN (...)
运行时策略装配:从配置驱动到注解声明
避免所有隔离逻辑都堆在拦截器里,把策略绑定交给更灵活的机制:
- 在Mapper接口方法上加自定义注解,如
@DataScope(type = DeptScope.class, includeSub = true) - 或通过YAML配置全局默认策略,再用
@ScopeOverride在特定方法上覆盖 - 结合Spring的
@ConditionalOnProperty,让不同环境启用不同隔离强度(如测试环境关闭,生产强制开启)
安全兜底与可观测性设计
再好的拦截器也可能被绕过,必须设置双重防线:
- 数据库层面开启行级安全策略(PostgreSQL的RLS或MySQL 8.0+的Row-Level Security插件),作为应用层拦截失败时的最后屏障
- 在拦截器中记录未命中任何策略的查询日志,触发告警;对
SELECT *或无WHERE条件的语句强制拒绝 - 暴露
/actuator/scope-status端点,实时查看当前租户激活了哪些维度策略、缓存命中率、SQL改写次数等指标










