From b0855cc626d2b9eb4495ea9e2d996df11d8dd090 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 12 Dec 2021 11:55:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20DataPermissionRuleFactoryI?= =?UTF-8?q?mpl=EF=BC=8C=E5=B9=B6=E5=A2=9E=E5=8A=A0=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=8D=95=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataPermissionAnnotationInterceptor.java | 4 +- .../core/aop/DataPermissionContextHolder.java | 43 ++++-- .../rule/DataPermissionRuleFactoryImpl.java | 41 ++++- .../resources/META-INF/spring.factories | 0 .../aop/DataPermissionContextHolderTest.java | 66 ++++++++ .../DataPermissionRuleFactoryImplTest.java | 145 ++++++++++++++++++ 6 files changed, 284 insertions(+), 15 deletions(-) rename yudao-framework/yudao-spring-boot-starter-data-permission/src/{test => main}/resources/META-INF/spring.factories (100%) create mode 100644 yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolderTest.java create mode 100644 yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java index 45917d912..48a03541f 100644 --- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java +++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionAnnotationInterceptor.java @@ -34,7 +34,7 @@ public class DataPermissionAnnotationInterceptor implements MethodInterceptor { // 入栈 DataPermission dataPermission = this.findAnnotation(methodInvocation); if (dataPermission != null) { - DataPermissionContextHolder.push(dataPermission); + DataPermissionContextHolder.add(dataPermission); } try { // 执行逻辑 @@ -42,7 +42,7 @@ public class DataPermissionAnnotationInterceptor implements MethodInterceptor { } finally { // 出栈 if (dataPermission != null) { - DataPermissionContextHolder.poll(); + DataPermissionContextHolder.remove(); } } } diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java index 02fd6e668..9705d88a9 100644 --- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java +++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolder.java @@ -3,8 +3,8 @@ 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; +import java.util.LinkedList; +import java.util.List; /** * {@link DataPermission} 注解的 Context 上下文 @@ -13,16 +13,19 @@ import java.util.Deque; */ public class DataPermissionContextHolder { - private static final ThreadLocal> DATA_PERMISSIONS = - TransmittableThreadLocal.withInitial(ArrayDeque::new); + /** + * 使用 List 的原因,可能存在方法的嵌套调用 + */ + private static final ThreadLocal> DATA_PERMISSIONS = + TransmittableThreadLocal.withInitial(LinkedList::new); /** * 获得当前的 DataPermission 注解 * * @return DataPermission 注解 */ - public static DataPermission peek() { - return DATA_PERMISSIONS.get().remove(); + public static DataPermission get() { + return DATA_PERMISSIONS.get().peekLast(); } /** @@ -30,8 +33,8 @@ public class DataPermissionContextHolder { * * @param dataPermission DataPermission 注解 */ - public static void push(DataPermission dataPermission) { - DATA_PERMISSIONS.get().push(dataPermission); + public static void add(DataPermission dataPermission) { + DATA_PERMISSIONS.get().addLast(dataPermission); } /** @@ -39,13 +42,31 @@ public class DataPermissionContextHolder { * * @return DataPermission 注解 */ - public static DataPermission poll() { - DataPermission dataPermission = DATA_PERMISSIONS.get().poll(); + public static DataPermission remove() { + DataPermission dataPermission = DATA_PERMISSIONS.get().removeLast(); // 无元素时,清空 ThreadLocal - if (dataPermission == null) { + if (DATA_PERMISSIONS.get().isEmpty()) { DATA_PERMISSIONS.remove(); } return dataPermission; } + /** + * 获得所有 DataPermission + * + * @return DataPermission 队列 + */ + public static List getAll() { + return DATA_PERMISSIONS.get(); + } + + /** + * 清空上下文 + * + * 目前仅仅用于单测 + */ + public static void clear() { + DATA_PERMISSIONS.remove(); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java index d4f7ee8b2..eaa6e6aed 100644 --- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImpl.java @@ -1,9 +1,21 @@ package cn.iocoder.yudao.framework.datapermission.core.rule; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder; import lombok.RequiredArgsConstructor; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +/** + * 默认的 DataPermissionRuleFactoryImpl 实现类 + * 支持通过 {@link DataPermissionContextHolder} 过滤数据权限 + * + * @author 芋道源码 + */ @RequiredArgsConstructor public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory { @@ -17,9 +29,34 @@ public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory return rules; } - @Override + @Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + DataPermission 进行缓存 public List getDataPermissionRule(String mappedStatementId) { - return null; + // 1. 无数据权限 + if (CollUtil.isEmpty(rules)) { + return Collections.emptyList(); + } + // 2. 未配置,则默认开启 + DataPermission dataPermission = DataPermissionContextHolder.get(); + if (dataPermission == null) { + return rules; + } + // 3. 已配置,但禁用 + if (!dataPermission.enable()) { + return Collections.emptyList(); + } + + // 4. 已配置,只选择部分规则 + if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) { + return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass())) + .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 + } + // 5. 已配置,只排除部分规则 + if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) { + return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass())) + .collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询 + } + // 6. 已配置,全部规则 + return rules; } } diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/resources/META-INF/spring.factories b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/resources/META-INF/spring.factories similarity index 100% rename from yudao-framework/yudao-spring-boot-starter-data-permission/src/test/resources/META-INF/spring.factories rename to yudao-framework/yudao-spring-boot-starter-data-permission/src/main/resources/META-INF/spring.factories diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolderTest.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolderTest.java new file mode 100644 index 000000000..688b92d9f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/aop/DataPermissionContextHolderTest.java @@ -0,0 +1,66 @@ +package cn.iocoder.yudao.framework.datapermission.core.aop; + +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; + +/** + * {@link DataPermissionContextHolder} 的单元测试 + * + * @author 芋道源码 + */ +class DataPermissionContextHolderTest { + + @BeforeEach + public void setUp() { + DataPermissionContextHolder.clear(); + } + + @Test + public void testGet() { + // mock 方法 + DataPermission dataPermission01 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission01); + DataPermission dataPermission02 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission02); + + // 调用 + DataPermission result = DataPermissionContextHolder.get(); + // 断言 + assertSame(result, dataPermission02); + } + + @Test + public void testPush() { + // 调用 + DataPermission dataPermission01 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission01); + DataPermission dataPermission02 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission02); + // 断言 + DataPermission first = DataPermissionContextHolder.getAll().get(0); + DataPermission second = DataPermissionContextHolder.getAll().get(1); + assertSame(dataPermission01, first); + assertSame(dataPermission02, second); + } + + @Test + public void testRemove() { + // mock 方法 + DataPermission dataPermission01 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission01); + DataPermission dataPermission02 = mock(DataPermission.class); + DataPermissionContextHolder.add(dataPermission02); + + // 调用 + DataPermission result = DataPermissionContextHolder.remove(); + // 断言 + assertSame(result, dataPermission02); + assertEquals(1, DataPermissionContextHolder.getAll().size()); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java new file mode 100644 index 000000000..17dddc929 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactoryImplTest.java @@ -0,0 +1,145 @@ +package cn.iocoder.yudao.framework.datapermission.core.rule; + +import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; +import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.springframework.core.annotation.AnnotationUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; + +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link DataPermissionRuleFactoryImpl} 单元测试 + * + * @author 芋道源码 + */ +class DataPermissionRuleFactoryImplTest extends BaseMockitoUnitTest { + + @InjectMocks + private DataPermissionRuleFactoryImpl dataPermissionRuleFactory; + + @Spy + private List rules = Arrays.asList(new DataPermissionRule01(), + new DataPermissionRule02()); + + @BeforeEach + public void setUp() { + DataPermissionContextHolder.clear(); + } + + @Test + public void testGetDataPermissionRule_02() { + // 准备参数 + String mappedStatementId = randomString(); + + // 调用 + List result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); + // 断言 + assertSame(rules, result); + } + + @Test + public void testGetDataPermissionRule_03() { + // 准备参数 + String mappedStatementId = randomString(); + // mock 方法 + DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass03.class, DataPermission.class)); + + // 调用 + List result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); + // 断言 + assertTrue(result.isEmpty()); + } + + @Test + public void testGetDataPermissionRule_04() { + // 准备参数 + String mappedStatementId = randomString(); + // mock 方法 + DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass04.class, DataPermission.class)); + + // 调用 + List result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); + // 断言 + assertEquals(1, result.size()); + assertEquals(DataPermissionRule01.class, result.get(0).getClass()); + } + + @Test + public void testGetDataPermissionRule_05() { + // 准备参数 + String mappedStatementId = randomString(); + // mock 方法 + DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass05.class, DataPermission.class)); + + // 调用 + List result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); + // 断言 + assertEquals(1, result.size()); + assertEquals(DataPermissionRule02.class, result.get(0).getClass()); + } + + @Test + public void testGetDataPermissionRule_06() { + // 准备参数 + String mappedStatementId = randomString(); + // mock 方法 + DataPermissionContextHolder.add(AnnotationUtils.findAnnotation(TestClass06.class, DataPermission.class)); + + // 调用 + List result = dataPermissionRuleFactory.getDataPermissionRule(mappedStatementId); + // 断言 + assertSame(rules, result); + } + + @DataPermission(enable = false) + static class TestClass03 {} + + @DataPermission(includeRules = DataPermissionRule01.class) + static class TestClass04 {} + + @DataPermission(excludeRules = DataPermissionRule01.class) + static class TestClass05 {} + + @DataPermission + static class TestClass06 {} + + static class DataPermissionRule01 implements DataPermissionRule { + + @Override + public Set getTableNames() { + return null; + } + + @Override + public Expression getExpression(String tableName, Alias tableAlias) { + return null; + } + + } + + static class DataPermissionRule02 implements DataPermissionRule { + + @Override + public Set getTableNames() { + return null; + } + + @Override + public Expression getExpression(String tableName, Alias tableAlias) { + return null; + } + + } + +}