Browse Source

1. 增加数据权限的自动配置 DataPermissionAutoConfiguration
2. 增加数据权限的 AOP DataPermissionAnnotationInterceptor
3. 重命名 DataPermissionDatabaseInterceptor

YunaiV 3 years ago
parent
commit
f9b15fe70d
11 changed files with 340 additions and 15 deletions
  1. 32 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java
  2. 36 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java
  3. 72 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java
  4. 51 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java
  5. 2 2
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java
  6. 0 1
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/package-info.java
  7. 25 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java
  8. 108 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java
  9. 7 7
      yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest.java
  10. 5 5
      yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java
  11. 2 0
      yudao-framework/yudao-spring-boot-starter-data-permission/src/test/resources/META-INF/spring.factories

+ 32 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/DataPermissionAutoConfiguration.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.datapermission.config;
+
+import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionAnnotationAdvisor;
+import cn.iocoder.yudao.framework.datapermission.core.db.DataPermissionDatabaseInterceptor;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactoryImpl;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+@Configuration
+public class DataPermissionAutoConfiguration {
+
+    @Bean
+    public DataPermissionRuleFactory dataPermissionRuleFactory(List<DataPermissionRule> rules) {
+        return new DataPermissionRuleFactoryImpl(rules);
+    }
+
+    @Bean
+    public DataPermissionDatabaseInterceptor dataPermissionDatabaseInterceptor(List<DataPermissionRule> rules) {
+        DataPermissionRuleFactory ruleFactory = dataPermissionRuleFactory(rules);
+        return new DataPermissionDatabaseInterceptor(ruleFactory);
+    }
+
+    @Bean
+    public DataPermissionAnnotationAdvisor dataPermissionAnnotationAdvisor() {
+        return new DataPermissionAnnotationAdvisor();
+    }
+
+}

+ 36 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationAdvisor.java

@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.framework.datapermission.core.aop;
+
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import org.aopalliance.aop.Advice;
+import org.springframework.aop.Pointcut;
+import org.springframework.aop.support.AbstractPointcutAdvisor;
+import org.springframework.aop.support.ComposablePointcut;
+import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
+
+/**
+ * {@link cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission} 注解的 Advisor 实现类
+ *
+ * @author 芋道源码
+ */
+@Getter
+@EqualsAndHashCode(callSuper = true)
+public class DataPermissionAnnotationAdvisor extends AbstractPointcutAdvisor {
+
+    private final Advice advice;
+
+    private final Pointcut pointcut;
+
+    public DataPermissionAnnotationAdvisor() {
+        this.advice = new DataPermissionAnnotationInterceptor();
+        this.pointcut = this.buildPointcut();
+    }
+
+    protected Pointcut buildPointcut() {
+        Pointcut classPointcut = new AnnotationMatchingPointcut(DataPermission.class, true);
+        Pointcut methodPointcut = new AnnotationMatchingPointcut(null, DataPermission.class, true);
+        return new ComposablePointcut(classPointcut).union(methodPointcut);
+    }
+
+}

+ 72 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java

@@ -0,0 +1,72 @@
+package cn.iocoder.yudao.framework.datapermission.core.aop;
+
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import lombok.Getter;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.core.MethodClassKey;
+import org.springframework.core.annotation.AnnotationUtils;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * {@link DataPermission} 注解的拦截器
+ * 1. 在执行方法前,将 @DataPermission 注解入栈
+ * 2. 在执行方法后,将 @DataPermission 注解出栈
+ *
+ * @author 芋道源码
+ */
+@DataPermission // 该注解,用于 {@link DATA_PERMISSION_NULL} 的空对象
+public class DataPermissionAnnotationInterceptor implements MethodInterceptor {
+
+    /**
+     * DataPermission 空对象,用于方法无 {@link DataPermission} 注解时,使用 DATA_PERMISSION_NULL 进行占位
+     */
+    static final DataPermission DATA_PERMISSION_NULL = DataPermissionAnnotationInterceptor.class.getAnnotation(DataPermission.class);
+
+    @Getter
+    private final Map<MethodClassKey, DataPermission> dataPermissionCache = new ConcurrentHashMap<>();
+
+    @Override
+    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+        // 入栈
+        DataPermission dataPermission = this.findAnnotation(methodInvocation);
+        if (dataPermission != null) {
+            DataPermissionContextHolder.push(dataPermission);
+        }
+        try {
+            // 执行逻辑
+            return methodInvocation.proceed();
+        } finally {
+            // 出栈
+            if (dataPermission != null) {
+                DataPermissionContextHolder.poll();
+            }
+        }
+    }
+
+    private DataPermission findAnnotation(MethodInvocation methodInvocation) {
+        // 1. 从缓存中获取
+        Method method = methodInvocation.getMethod();
+        Object targetObject = methodInvocation.getThis();
+        Class<?> clazz = targetObject != null ? targetObject.getClass() : method.getDeclaringClass();
+        MethodClassKey methodClassKey = new MethodClassKey(method, clazz);
+        DataPermission dataPermission = dataPermissionCache.get(methodClassKey);
+        if (dataPermission != null) {
+            return dataPermission != DATA_PERMISSION_NULL ? dataPermission : null;
+        }
+
+        // 2.1 从方法中获取
+        dataPermission = AnnotationUtils.findAnnotation(method, DataPermission.class);
+        // 2.2 从类上获取
+        if (dataPermission == null) {
+            dataPermission = AnnotationUtils.findAnnotation(clazz, DataPermission.class);
+        }
+        // 2.3 添加到缓存中
+        dataPermissionCache.put(methodClassKey, dataPermission != null ? dataPermission : DATA_PERMISSION_NULL);
+        return dataPermission;
+    }
+
+}

+ 51 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java

@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.framework.datapermission.core.aop;
+
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import com.alibaba.ttl.TransmittableThreadLocal;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+/**
+ * {@link DataPermission} 注解的 Context 上下文
+ *
+ * @author 芋道源码
+ */
+public class DataPermissionContextHolder {
+
+    private static final ThreadLocal<Deque<DataPermission>> DATA_PERMISSIONS =
+            TransmittableThreadLocal.withInitial(ArrayDeque::new);
+
+    /**
+     * 获得当前的 DataPermission 注解
+     *
+     * @return DataPermission 注解
+     */
+    public static DataPermission peek() {
+        return DATA_PERMISSIONS.get().remove();
+    }
+
+    /**
+     * 入栈 DataPermission 注解
+     *
+     * @param dataPermission DataPermission 注解
+     */
+    public static void push(DataPermission dataPermission) {
+        DATA_PERMISSIONS.get().push(dataPermission);
+    }
+
+    /**
+     * 出栈 DataPermission 注解
+     *
+     * @return DataPermission 注解
+     */
+    public static DataPermission poll() {
+        DataPermission dataPermission = DATA_PERMISSIONS.get().poll();
+        // 无元素时,清空 ThreadLocal
+        if (dataPermission == null) {
+            DATA_PERMISSIONS.remove();
+        }
+        return dataPermission;
+    }
+
+}

+ 2 - 2
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java → yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptor.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.datapermission.core.interceptor;
+package cn.iocoder.yudao.framework.datapermission.core.db;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
@@ -45,7 +45,7 @@ import java.util.concurrent.ConcurrentHashMap;
  * @author 芋道源码
  */
 @RequiredArgsConstructor
-public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
+public class DataPermissionDatabaseInterceptor extends JsqlParserSupport implements InnerInterceptor {
 
     private final DataPermissionRuleFactory ruleFactory;
 

+ 0 - 1
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/package-info.java

@@ -1 +0,0 @@
-package cn.iocoder.yudao.framework.datapermission.core;

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java

@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.framework.datapermission.core.rule;
+
+import lombok.RequiredArgsConstructor;
+
+import java.util.List;
+
+@RequiredArgsConstructor
+public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory {
+
+    /**
+     * 数据权限规则数组
+     */
+    private final List<DataPermissionRule> rules;
+
+    @Override
+    public List<DataPermissionRule> getDataPermissionRules() {
+        return rules;
+    }
+
+    @Override
+    public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {
+        return null;
+    }
+
+}

+ 108 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptorTest.java

@@ -0,0 +1,108 @@
+package cn.iocoder.yudao.framework.datapermission.core.aop;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
+import org.aopalliance.intercept.MethodInvocation;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+
+import java.lang.reflect.Method;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.when;
+
+/**
+ * {@link DataPermissionAnnotationInterceptor} 的单元测试
+ *
+ * @author 芋道源码
+ */
+public class DataPermissionAnnotationInterceptorTest extends BaseMockitoUnitTest {
+
+    @InjectMocks
+    private DataPermissionAnnotationInterceptor interceptor;
+
+    @Mock
+    private MethodInvocation methodInvocation;
+
+    @BeforeEach
+    public void setUp() {
+        interceptor.getDataPermissionCache().clear();
+    }
+
+    @Test // 无 @DataPermission 注解
+    public void testInvoke_none() throws Throwable {
+        // 参数
+        mockMethodInvocation(TestNone.class);
+
+        // 调用
+        Object result = interceptor.invoke(methodInvocation);
+        // 断言
+        assertEquals("none", result);
+        assertEquals(1, interceptor.getDataPermissionCache().size());
+        assertTrue(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());
+    }
+
+    @Test // 在 Method 上有 @DataPermission 注解
+    public void testInvoke_method() throws Throwable {
+        // 参数
+        mockMethodInvocation(TestMethod.class);
+
+        // 调用
+        Object result = interceptor.invoke(methodInvocation);
+        // 断言
+        assertEquals("method", result);
+        assertEquals(1, interceptor.getDataPermissionCache().size());
+        assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());
+    }
+
+    @Test // 在 Class 上有 @DataPermission 注解
+    public void testInvoke_class() throws Throwable {
+        // 参数
+        mockMethodInvocation(TestClass.class);
+
+        // 调用
+        Object result = interceptor.invoke(methodInvocation);
+        // 断言
+        assertEquals("class", result);
+        assertEquals(1, interceptor.getDataPermissionCache().size());
+        assertFalse(CollUtil.getFirst(interceptor.getDataPermissionCache().values()).enable());
+    }
+
+    private void mockMethodInvocation(Class<?> clazz) throws Throwable {
+        Object targetObject = clazz.newInstance();
+        Method method = targetObject.getClass().getMethod("echo");
+        when(methodInvocation.getThis()).thenReturn(targetObject);
+        when(methodInvocation.getMethod()).thenReturn(method);
+        when(methodInvocation.proceed()).then(invocationOnMock -> method.invoke(targetObject));
+    }
+
+    static class TestMethod {
+
+        @DataPermission(enable = false)
+        public String echo() {
+            return "method";
+        }
+
+    }
+
+    @DataPermission(enable = false)
+    static class TestClass {
+
+        public String echo() {
+            return "class";
+        }
+
+    }
+
+    static class TestNone {
+
+        public String echo() {
+            return "none";
+        }
+
+    }
+
+}

+ 7 - 7
yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest.java → yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.datapermission.core.interceptor;
+package cn.iocoder.yudao.framework.datapermission.core.db;
 
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
@@ -29,17 +29,17 @@ import static org.junit.jupiter.api.Assertions.*;
 import static org.mockito.Mockito.*;
 
 /**
- * {@link DataPermissionInterceptor} 的单元测试
- * 主要测试 {@link DataPermissionInterceptor#beforePrepare(StatementHandler, Connection, Integer)}
- * 和 {@link DataPermissionInterceptor#beforeUpdate(Executor, MappedStatement, Object)}
+ * {@link DataPermissionDatabaseInterceptor} 的单元测试
+ * 主要测试 {@link DataPermissionDatabaseInterceptor#beforePrepare(StatementHandler, Connection, Integer)}
+ * 和 {@link DataPermissionDatabaseInterceptor#beforeUpdate(Executor, MappedStatement, Object)}
  * 以及在这个过程中,ContextHolder 和 MappedStatementCache
  *
  * @author 芋道源码
  */
-public class DataPermissionInterceptorTest extends BaseMockitoUnitTest {
+public class DataPermissionDatabaseInterceptorTest extends BaseMockitoUnitTest {
 
     @InjectMocks
-    private DataPermissionInterceptor interceptor;
+    private DataPermissionDatabaseInterceptor interceptor;
 
     @Mock
     private DataPermissionRuleFactory ruleFactory;
@@ -47,7 +47,7 @@ public class DataPermissionInterceptorTest extends BaseMockitoUnitTest {
     @BeforeEach
     public void setUp() {
         // 清理上下文
-        DataPermissionInterceptor.ContextHolder.clear();
+        DataPermissionDatabaseInterceptor.ContextHolder.clear();
         // 清空缓存
         interceptor.getMappedStatementCache().clear();
     }

+ 5 - 5
yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest2.java → yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/db/DataPermissionDatabaseInterceptorTest2.java

@@ -1,4 +1,4 @@
-package cn.iocoder.yudao.framework.datapermission.core.interceptor;
+package cn.iocoder.yudao.framework.datapermission.core.db;
 
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
 import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
@@ -23,16 +23,16 @@ import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- * {@link DataPermissionInterceptor} 的单元测试
+ * {@link DataPermissionDatabaseInterceptor} 的单元测试
  * 主要复用了 MyBatis Plus 的 TenantLineInnerInterceptorTest 的单元测试
  * 不过它的单元测试不是很规范,考虑到是复用的,所以暂时不进行修改~
  *
  * @author 芋道源码
  */
-public class DataPermissionInterceptorTest2 extends BaseMockitoUnitTest {
+public class DataPermissionDatabaseInterceptorTest2 extends BaseMockitoUnitTest {
 
     @InjectMocks
-    private DataPermissionInterceptor interceptor;
+    private DataPermissionDatabaseInterceptor interceptor;
 
     @Mock
     private DataPermissionRuleFactory ruleFactory;
@@ -78,7 +78,7 @@ public class DataPermissionInterceptorTest2 extends BaseMockitoUnitTest {
 
         };
         // 设置到上下文,保证
-        DataPermissionInterceptor.ContextHolder.init(Arrays.asList(tenantRule, deptRule));
+        DataPermissionDatabaseInterceptor.ContextHolder.init(Arrays.asList(tenantRule, deptRule));
     }
 
     @Test

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-data-permission/src/test/resources/META-INF/spring.factories

@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+  cn.iocoder.yudao.framework.datapermission.config.DataPermissionAutoConfiguration