From 58b1bf5c33920f1d6f19f9657563e8adbfc1616f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A7=8B=E8=BE=9E=E6=9C=AA=E5=AF=92?= <545073804@qq.com> Date: Tue, 29 Jul 2025 03:25:59 +0000 Subject: [PATCH] =?UTF-8?q?!731=20update=20=E6=8A=8A=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=9D=83=E9=99=90=E6=B3=A8=E8=A7=A3=E5=85=A8=E9=83=A8=E4=BA=A4?= =?UTF-8?q?=E7=BB=99AOP=E5=A4=84=E7=90=86=EF=BC=8C=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E5=8A=A8=E6=80=81=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E5=99=A8=E5=8C=B9=E9=85=8D=E6=B3=A8=E8=A7=A3?= =?UTF-8?q?=20*=20update=20=E6=8A=8A=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=85=A8=E9=83=A8=E4=BA=A4=E7=BB=99AOP?= =?UTF-8?q?=E5=A4=84=E7=90=86=EF=BC=8C=E4=BD=BF=E7=94=A8=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E5=8A=A8=E6=80=81=E6=96=B9=E6=B3=95=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E5=99=A8=E5=8C=B9=E9=85=8D=E6=B3=A8=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mybatis/aop/DataPermissionAdvice.java | 54 +++++++++++ .../DataPermissionDynamicMethodMatcher.java | 46 +++++++++ .../aop/DataPermissionPointcutAdvisor.java | 38 ++++++++ .../mybatis/aspect/DataPermissionAspect.java | 50 ---------- .../mybatis/config/MybatisPlusConfig.java | 11 ++- .../handler/PlusDataPermissionHandler.java | 97 ++----------------- .../PlusDataPermissionInterceptor.java | 21 ++-- 7 files changed, 157 insertions(+), 160 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionAdvice.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionDynamicMethodMatcher.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionPointcutAdvisor.java delete mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAspect.java diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionAdvice.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionAdvice.java new file mode 100644 index 000000000..468b1d5b5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionAdvice.java @@ -0,0 +1,54 @@ +package org.dromara.common.mybatis.aop; + +import lombok.extern.slf4j.Slf4j; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.dromara.common.mybatis.helper.DataPermissionHelper; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 数据权限注解Advice + * + * @author 秋辞未寒 + */ +@Slf4j +public class DataPermissionAdvice implements MethodInterceptor { + + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + Object target = invocation.getThis(); + Method method = invocation.getMethod(); + Object[] args = invocation.getArguments(); + // 设置权限注解 + DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method, args)); + try { + // 执行代理方法 + return invocation.proceed(); + } finally { + // 清除权限注解 + DataPermissionHelper.removePermission(); + } + } + + /** + * 获取数据权限注解 + */ + private DataPermission getDataPermissionAnnotation(Object target, Method method,Object[] args){ + DataPermission dataPermission = method.getAnnotation(DataPermission.class); + // 优先获取方法上的注解 + if (dataPermission != null) { + return dataPermission; + } + // 方法上没有注解,则获取类上的注解 + Class targetClass = target.getClass(); + // 如果是 JDK 动态代理,则获取真实的Class实例 + if (Proxy.isProxyClass(targetClass)) { + targetClass = targetClass.getInterfaces()[0]; + } + dataPermission = targetClass.getAnnotation(DataPermission.class); + return dataPermission; + } +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionDynamicMethodMatcher.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionDynamicMethodMatcher.java new file mode 100644 index 000000000..a79e27a23 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionDynamicMethodMatcher.java @@ -0,0 +1,46 @@ +package org.dromara.common.mybatis.aop; + +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.springframework.aop.support.DynamicMethodMatcher; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 数据权限动态方法匹配器 + * + * @author 秋辞未寒 + */ +@Slf4j +@SuppressWarnings("all") +public class DataPermissionDynamicMethodMatcher extends DynamicMethodMatcher { + + public DataPermissionDynamicMethodMatcher() {} + + @Override + public boolean matches(Method method, Class targetClass) { + // 优先匹配方法 + // 数据权限注解不对继承生效,所以检查当前方法是否有注解即可,不再往上匹配父类或接口 + if (method.isAnnotationPresent(DataPermission.class)) { + return true; + } + + // MyBatis 的 Mapper 就是通过 JDK 动态代理实现的,所以这里需要检查是否匹配 JDK 的动态代理 + Class targetClassRef = targetClass; + if (Proxy.isProxyClass(targetClassRef)) { + // 数据权限注解不对继承生效,但由于 SpringIOC 容器拿到的实际上是 MyBatis 代理过后的 Mapper,而 targetClass.isAnnotationPresent 实际匹配的是 Proxy 类的注解,不会查找代理类。 + // 所以这里不能用 targetClass.isAnnotationPresent,只能用 AnnotatedElementUtils.hasAnnotation 或 targetClass.getInterfaces()[0].isAnnotationPresent 去做匹配,以检查被代理的 MapperClass 是否具有注解 + // 原理:JDK 动态代理本质上就是对接口进行实现然后对具体的接口实现做代理,所以直接通过接口可以拿到实际的 MapperClass + targetClassRef = targetClass.getInterfaces()[0]; + + } + return targetClassRef.isAnnotationPresent(DataPermission.class); + } + + @Override + public boolean matches(Method method, Class targetClass, Object... args) { + return matches(method, targetClass); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionPointcutAdvisor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionPointcutAdvisor.java new file mode 100644 index 000000000..17cc90bef --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aop/DataPermissionPointcutAdvisor.java @@ -0,0 +1,38 @@ +package org.dromara.common.mybatis.aop; + +import org.aopalliance.aop.Advice; +import org.dromara.common.mybatis.annotation.DataPermission; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; +import org.springframework.aop.support.ComposablePointcut; +import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; + +/** + * 数据权限注解切面定义 + * + * @author 秋辞未寒 + */ +@SuppressWarnings("all") +public class DataPermissionPointcutAdvisor extends AbstractPointcutAdvisor { + + private final Advice advice; + private final Pointcut pointcut; + + public DataPermissionPointcutAdvisor() { + this.advice = new DataPermissionAdvice(); + AnnotationMatchingPointcut matchingPointcut = new AnnotationMatchingPointcut(DataPermission.class, true); + DataPermissionDynamicMethodMatcher matcher = new DataPermissionDynamicMethodMatcher(); + this.pointcut = new ComposablePointcut(matcher).union(matchingPointcut); + } + + @Override + public Pointcut getPointcut() { + return this.pointcut; + } + + @Override + public Advice getAdvice() { + return this.advice; + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAspect.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAspect.java deleted file mode 100644 index 1c83cc392..000000000 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAspect.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.dromara.common.mybatis.aspect; - -import lombok.extern.slf4j.Slf4j; -import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.AfterThrowing; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; -import org.dromara.common.mybatis.annotation.DataPermission; -import org.dromara.common.mybatis.helper.DataPermissionHelper; - -/** - * 数据权限处理 - * - * @author Lion Li - */ -@Slf4j -@Aspect -public class DataPermissionAspect { - - /** - * 处理请求前执行 - */ - @Before(value = "@annotation(dataPermission)") - public void doBefore(JoinPoint joinPoint, DataPermission dataPermission) { - DataPermissionHelper.setPermission(dataPermission); - } - - /** - * 处理完请求后执行 - * - * @param joinPoint 切点 - */ - @AfterReturning(pointcut = "@annotation(dataPermission)") - public void doAfterReturning(JoinPoint joinPoint, DataPermission dataPermission) { - DataPermissionHelper.removePermission(); - } - - /** - * 拦截异常操作 - * - * @param joinPoint 切点 - * @param e 异常 - */ - @AfterThrowing(value = "@annotation(dataPermission)", throwing = "e") - public void doAfterThrowing(JoinPoint joinPoint, DataPermission dataPermission, Exception e) { - DataPermissionHelper.removePermission(); - } - -} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java index 00c26912e..84173d610 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/config/MybatisPlusConfig.java @@ -11,15 +11,18 @@ import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerIntercept import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import org.dromara.common.core.factory.YmlPropertySourceFactory; import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.mybatis.aspect.DataPermissionAspect; +import org.dromara.common.mybatis.aop.DataPermissionPointcutAdvisor; import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler; import org.dromara.common.mybatis.handler.MybatisExceptionHandler; import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler; import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.PropertySource; +import org.springframework.context.annotation.Role; import org.springframework.transaction.annotation.EnableTransactionManagement; /** @@ -54,15 +57,15 @@ public class MybatisPlusConfig { * 数据权限拦截器 */ public PlusDataPermissionInterceptor dataPermissionInterceptor() { - return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage")); + return new PlusDataPermissionInterceptor(); } /** * 数据权限切面处理器 */ @Bean - public DataPermissionAspect dataPermissionAspect() { - return new DataPermissionAspect(); + public DataPermissionPointcutAdvisor dataPermissionPointcutAdvisor() { + return new DataPermissionPointcutAdvisor(); } /** diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java index 6dee1214a..98b89e9ed 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/PlusDataPermissionHandler.java @@ -49,11 +49,6 @@ import java.util.function.Function; @Slf4j public class PlusDataPermissionHandler { - /** - * 类名称与注解的映射关系缓存(由于aop无法拦截mybatis接口类上的注解 只能通过启动预扫描的方式进行) - */ - private final Map dataPermissionCacheMap = new ConcurrentHashMap<>(); - /** * spel 解析器 */ @@ -64,27 +59,17 @@ public class PlusDataPermissionHandler { */ private final BeanResolver beanResolver = new BeanFactoryResolver(SpringUtils.getBeanFactory()); - /** - * 构造方法,扫描指定包下的 Mapper 类并初始化缓存 - * - * @param mapperPackage Mapper 类所在的包路径 - */ - public PlusDataPermissionHandler(String mapperPackage) { - scanMapperClasses(mapperPackage); - } - /** * 获取数据过滤条件的 SQL 片段 * * @param where 原始的查询条件表达式 - * @param mappedStatementId Mapper 方法的 ID * @param isSelect 是否为查询语句 * @return 数据过滤条件的 SQL 片段 */ - public Expression getSqlSegment(Expression where, String mappedStatementId, boolean isSelect) { + public Expression getSqlSegment(Expression where, boolean isSelect) { try { // 获取数据权限配置 - DataPermission dataPermission = getDataPermission(mappedStatementId); + DataPermission dataPermission = getDataPermission(); // 获取当前登录用户信息 LoginUser currentUser = DataPermissionHelper.getVariable("user"); if (ObjectUtil.isNull(currentUser)) { @@ -206,92 +191,22 @@ public class PlusDataPermissionHandler { return StringUtils.EMPTY; } - /** - * 扫描指定包下的 Mapper 类,并查找其中带有特定注解的方法或类 - * - * @param mapperPackage Mapper 类所在的包路径 - */ - private void scanMapperClasses(String mapperPackage) { - // 创建资源解析器和元数据读取工厂 - PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); - CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); - // 将 Mapper 包路径按分隔符拆分为数组 - String[] packagePatternArray = StringUtils.splitPreserveAllTokens(mapperPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); - String classpath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX; - try { - for (String packagePattern : packagePatternArray) { - // 将包路径转换为资源路径 - String path = ClassUtils.convertClassNameToResourcePath(packagePattern); - // 获取指定路径下的所有 .class 文件资源 - Resource[] resources = resolver.getResources(classpath + path + "/*.class"); - for (Resource resource : resources) { - // 获取资源的类元数据 - ClassMetadata classMetadata = factory.getMetadataReader(resource).getClassMetadata(); - // 获取资源对应的类对象 - Class clazz = Resources.classForName(classMetadata.getClassName()); - // 查找类中的特定注解 - findAnnotation(clazz); - } - } - } catch (Exception e) { - log.error("初始化数据安全缓存时出错:{}", e.getMessage()); - } - } - - /** - * 在指定的类中查找特定的注解 DataPermission,并将带有这个注解的方法或类存储到 dataPermissionCacheMap 中 - * - * @param clazz 要查找的类 - */ - private void findAnnotation(Class clazz) { - DataPermission dataPermission; - for (Method method : clazz.getMethods()) { - if (method.isDefault() || method.isVarArgs()) { - continue; - } - String mappedStatementId = clazz.getName() + "." + method.getName(); - if (AnnotationUtil.hasAnnotation(method, DataPermission.class)) { - dataPermission = AnnotationUtil.getAnnotation(method, DataPermission.class); - dataPermissionCacheMap.put(mappedStatementId, dataPermission); - } - } - if (AnnotationUtil.hasAnnotation(clazz, DataPermission.class)) { - dataPermission = AnnotationUtil.getAnnotation(clazz, DataPermission.class); - dataPermissionCacheMap.put(clazz.getName(), dataPermission); - } - } - /** * 根据映射语句 ID 或类名获取对应的 DataPermission 注解对象 * - * @param mapperId 映射语句 ID * @return DataPermission 注解对象,如果不存在则返回 null */ - public DataPermission getDataPermission(String mapperId) { - // 检查上下文中是否包含映射语句 ID 对应的 DataPermission 注解对象 - if (DataPermissionHelper.getPermission() != null) { - return DataPermissionHelper.getPermission(); - } - // 检查缓存中是否包含映射语句 ID 对应的 DataPermission 注解对象 - if (dataPermissionCacheMap.containsKey(mapperId)) { - return dataPermissionCacheMap.get(mapperId); - } - // 如果缓存中不包含映射语句 ID 对应的 DataPermission 注解对象,则尝试使用类名作为键查找 - String clazzName = mapperId.substring(0, mapperId.lastIndexOf(".")); - if (dataPermissionCacheMap.containsKey(clazzName)) { - return dataPermissionCacheMap.get(clazzName); - } - return null; + public DataPermission getDataPermission() { + return DataPermissionHelper.getPermission(); } /** * 检查给定的映射语句 ID 是否有效,即是否能够找到对应的 DataPermission 注解对象 * - * @param mapperId 映射语句 ID * @return 如果找到对应的 DataPermission 注解对象,则返回 false;否则返回 true */ - public boolean invalid(String mapperId) { - return getDataPermission(mapperId) == null; + public boolean invalid() { + return getDataPermission() == null; } /** diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java index 85a4d0abc..b37d96ed1 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/interceptor/PlusDataPermissionInterceptor.java @@ -35,16 +35,7 @@ import java.util.List; @Slf4j public class PlusDataPermissionInterceptor extends BaseMultiTableInnerInterceptor implements InnerInterceptor { - private final PlusDataPermissionHandler dataPermissionHandler; - - /** - * 构造函数,初始化 PlusDataPermissionHandler 实例 - * - * @param mapperPackage 扫描的映射器包 - */ - public PlusDataPermissionInterceptor(String mapperPackage) { - this.dataPermissionHandler = new PlusDataPermissionHandler(mapperPackage); - } + private final PlusDataPermissionHandler dataPermissionHandler = new PlusDataPermissionHandler(); /** * 在执行查询之前,检查并处理数据权限相关逻辑 @@ -64,7 +55,7 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto return; } // 检查是否缺少有效的数据权限注解 - if (dataPermissionHandler.invalid(ms.getId())) { + if (dataPermissionHandler.invalid()) { return; } // 解析 sql 分配对应方法 @@ -92,7 +83,7 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto return; } // 检查是否缺少有效的数据权限注解 - if (dataPermissionHandler.invalid(ms.getId())) { + if (dataPermissionHandler.invalid()) { return; } PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql(); @@ -128,7 +119,7 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto */ @Override protected void processUpdate(Update update, int index, String sql, Object obj) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), (String) obj, false); + Expression sqlSegment = dataPermissionHandler.getSqlSegment(update.getWhere(), false); if (null != sqlSegment) { update.setWhere(sqlSegment); } @@ -144,7 +135,7 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto */ @Override protected void processDelete(Delete delete, int index, String sql, Object obj) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), (String) obj, false); + Expression sqlSegment = dataPermissionHandler.getSqlSegment(delete.getWhere(), false); if (null != sqlSegment) { delete.setWhere(sqlSegment); } @@ -157,7 +148,7 @@ public class PlusDataPermissionInterceptor extends BaseMultiTableInnerIntercepto * @param mappedStatementId 映射语句的 ID */ protected void setWhere(PlainSelect plainSelect, String mappedStatementId) { - Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), mappedStatementId, true); + Expression sqlSegment = dataPermissionHandler.getSqlSegment(plainSelect.getWhere(), true); if (null != sqlSegment) { plainSelect.setWhere(sqlSegment); }