news 2026/4/15 13:36:19

如何设计一个@TenantIgnore功能,使得被该注解注解的方法可以使用户绕过自己的租户id查询全局信息(所有租户的信息)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何设计一个@TenantIgnore功能,使得被该注解注解的方法可以使用户绕过自己的租户id查询全局信息(所有租户的信息)

什么是@TelnantIgnore

有些系统设计时,将不同公司(租户)的数据放在同一个数据库(表)中,因此会员A在查询数据时,应只查询本人所在公司的数据,因此会在某些表每个执行的sql语句中默认加上"where tenantid = ?"来限制用户的行为。但是也有一些全局的查询,要查询所有公司的所有数据,因此需要在该Controller的方法上加上@TelnantIgnore,使该方法绕过加"where tenantid = ?"这个步骤。


如何做

这是一个非常经典的AOP + ThreadLocal + ORM 插件协同工作的架构设计案例。

如果你要从零设计一个@TenantIgnore功能,让被注解的方法能够绕过租户限制查询全局数据,你需要构建一条完整的指令传输链

我们可以把这个过程分为4 个核心步骤,就像设计一套“免检通行”系统:


第一步:制作通行证(定义注解)

首先,你需要定义一个注解,作为一个“标记”。

// 1. 定义注解 @TenantIgnore@Target({ElementType.METHOD,ElementType.TYPE})// 既能加在方法上,也能加在类上@Retention(RetentionPolicy.RUNTIME)// 运行时必须存在public@interfaceTenantIgnore{// 可以扩展属性,比如 boolean value() default true;}

第二步:准备口袋(定义上下文 Holder)

你需要一个地方来存放“是否免检”这个状态。因为 Web 请求是基于线程的,所以必须用ThreadLocal

// 2. 定义上下文容器 TenantContextHolderpublicclassTenantContextHolder{// 使用 ThreadLocal 保证线程隔离privatestaticfinalThreadLocal<Boolean>IGNORE=newThreadLocal<>();// 设置状态publicstaticvoidsetIgnore(Booleanignore){IGNORE.set(ignore);}// 获取状态(供 MyBatis 插件读取)publicstaticbooleanisIgnore(){returnBoolean.TRUE.equals(IGNORE.get());}// 清理(防止内存泄漏和线程污染)publicstaticvoidclear(){IGNORE.remove();}}

第三步:门口安检员(编写 AOP 切面)

这是核心逻辑。你需要拦截所有贴了@TenantIgnore的方法,在方法执行把“免检卡”塞进口袋,执行把卡拿出来。

// 3. 定义切面 TenantIgnoreAspect@Aspect@ComponentpublicclassTenantIgnoreAspect{@Around("@annotation(tenantIgnore)")// 拦截所有带 @TenantIgnore 的方法publicObjectaround(ProceedingJoinPointjoinPoint,TenantIgnoretenantIgnore)throwsThrowable{// A. 存档:先记住进门前的状态(防止嵌套调用时,把外层的状态搞丢)booleanoldIgnore=TenantContextHolder.isIgnore();try{// B. 塞卡:标记当前线程为“忽略租户”TenantContextHolder.setIgnore(true);// C. 放行:执行真正的业务逻辑(查询数据库)returnjoinPoint.proceed();}finally{// D. 复原:无论成功失败,必须把状态恢复成进门前的样子// 这一步至关重要!否则线程池复用会导致下一个请求数据泄露!TenantContextHolder.setIgnore(oldIgnore);}}}

第四步:修改数据库规则(配置 MyBatis Plus Handler)

最后,你需要告诉 MyBatis Plus:“生成 SQL 之前,先去口袋里摸一下,看看有没有免检卡。”

// 4. 配置 MyBatis Plus 的多租户拦截器@ComponentpublicclassMyTenantLineHandlerimplementsTenantLineHandler{@OverridepublicExpressiongetTenantId(){// 返回当前用户的租户ID,例如 1returnnewLongValue(UserContext.getTenantId());}@OverridepublicStringgetTenantIdColumn(){return"tenant_id";// 数据库里的列名}// 【核心】决定是否要忽略表(不加 WHERE tenant_id = ?)@OverridepublicbooleanignoreTable(StringtableName){// 1. 系统表(如字典、菜单)默认忽略if("sys_dict".equals(tableName)){returntrue;}// 2. 【关键】去口袋(ThreadLocal)里查状态// 如果 Aspect 设置了 true,这里就返回 true -> MyBatis Plus 就不会拼接租户条件了if(TenantContextHolder.isIgnore()){returntrue;}// 3. 默认不忽略returnfalse;}}

🌟 总结:完整的执行流

当你写下这段代码时:

@TenantIgnore// 1. 贴上标记publicList<User>getAllUsers(){returnuserMapper.selectList(null);// 2. 查库}

系统底层发生了什么:

  1. AOP 介入:Aspect 看到注解,调用TenantContextHolder.setIgnore(true)。此时当前线程的ThreadLocal变为true
  2. 业务执行:代码跑到userMapper.selectList
  3. MP 拦截:MyBatis Plus 准备生成 SQL,问MyTenantLineHandler:“我要不要加租户条件?”
  4. Handler 检查:Handler 调用TenantContextHolder.isIgnore(),发现是true
  5. SQL 生成:Handler 说“不用加”。MP 生成SELECT * FROM user(全表查询)。
  6. AOP 收尾:方法返回,Aspect 在finally块中清理ThreadLocal

这就是通过注解驱动实现动态数据权限控制的标准架构设计。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 13:35:42

28、红帽 Linux 网络服务搭建与 Apache 服务器配置指南

红帽 Linux 网络服务搭建与 Apache 服务器配置指南 1. 红帽 Linux 的优势与适用场景 红帽 Linux 在网络应用方面具有诸多优势,使其成为许多用户的选择。 1.1 总体拥有成本(TCO)优势 购买成本低 :操作系统和软件本身的购买成本较低。 安装成本有利 :借助网络应用的最…

作者头像 李华
网站建设 2026/4/12 17:04:23

37、深入探究:Linux内核的重建与优化

深入探究:Linux内核的重建与优化 1. Linux内核概述 Linux内核作为Linux操作系统的核心,承担着诸多关键职责。它不仅为底层硬件提供接口,处理应用程序与硬件间的所有通信,还负责一系列重要任务,具体如下: - 进程管理 :涵盖进程的创建、调度和终止。 - 进程间通信 …

作者头像 李华
网站建设 2026/4/14 5:53:57

43、Red Hat系统安全维护与监控指南

Red Hat系统安全维护与监控指南 1. 密码破解警告 在使用密码破解工具(如Crack)时,如果不使用 -quiet 开关,会收到关于锁定账户和无有效密码账户的警告信息。像用于运行打印和Web服务(如lpd和apache)的账户,通常会配置成这样,以防止他人以这些用户身份登录。 重要提…

作者头像 李华
网站建设 2026/4/14 5:52:13

EmotiVoice语音合成在互动故事游戏中的分支语音生成

EmotiVoice语音合成在互动故事游戏中的分支语音生成 在一款互动叙事游戏中&#xff0c;玩家的选择不再只是决定剧情走向——它还能实时改变角色的语气、情绪甚至声音本身。当主角从信任走向背叛&#xff0c;他的语调由温和转为冰冷&#xff1b;当配角突然揭露隐藏身份&#xff…

作者头像 李华
网站建设 2026/4/14 5:51:01

18、量子认知、理性与共同知识:经典与量子视角的探讨

量子认知、理性与共同知识:经典与量子视角的探讨 1. 奥曼定理与量子理性 奥曼定理在决策和博弈论中具有重要地位,但它存在一些潜在假设。除了常见的共同知识和共同先验假设外,还存在一个隐藏假设,即参与者的理性。经典理性概念基于贝叶斯推理,在科学理论中通过经典概率(…

作者头像 李华
网站建设 2026/4/14 5:49:55

C++ 继承 多态 组合 从运用到底层原理详解!草履虫也能轻松看懂

这篇blog我写了一天一夜, 但是我初学时花了好几个月才学懂 , 我想告诉你, 我那个时候也非常痛苦, 数次想要放弃, 我花了好几天时间才搞懂虚函数原理, 我真是个很笨的人, 只能花很多时间一点一点理解这些东西. 学习C的过程很艰辛, 不要被困难打倒, 一定要坚持下去!没有任何困难是…

作者头像 李华