diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/pom.xml b/yudao-framework/yudao-spring-boot-starter-data-permission/pom.xml
index 8fed34af4..fac3234c1 100644
--- a/yudao-framework/yudao-spring-boot-starter-data-permission/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-data-permission/pom.xml
@@ -29,8 +29,8 @@
- org.springframework.boot
- spring-boot-starter-test
+ cn.iocoder.boot
+ yudao-spring-boot-starter-test
test
diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java
index f9f8943d9..a1af259e0 100644
--- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java
+++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptor.java
@@ -1,11 +1,16 @@
package cn.iocoder.yudao.framework.datapermission.core.interceptor;
-import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper;
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRule;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
+import com.alibaba.ttl.TransmittableThreadLocal;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import lombok.RequiredArgsConstructor;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
@@ -24,33 +29,58 @@ import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+@RequiredArgsConstructor
public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {
-// private TenantLineHandler tenantLineHandler;
+ private final DataPermissionRuleFactory ruleFactory;
- @Override
+ private final MappedStatementCache mappedStatementCache = new MappedStatementCache();
+
+ @Override // SELECT 场景
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
- // TODO 芋艿:这个判断,后续读懂下
- if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) return;
+ // 获得 Mapper 对应的数据权限的规则
+ List rules = ruleFactory.getDataPermissionRule(ms.getId());
+ if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
+ return;
+ }
+
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
- // TODO 芋艿:null=》DataScope
- mpBs.sql(parserSingle(mpBs.sql(), null));
+ try {
+ // 初始化上下文
+ ContextHolder.init(rules);
+ // 处理 SQL
+ mpBs.sql(parserSingle(mpBs.sql(), null));
+ } finally {
+ addMappedStatementCache(ms);
+ ContextHolder.clear();
+ }
}
- @Override
+ @Override // 只处理 UPDATE / DELETE 场景
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
if (sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) { // 无需处理 Insert 语句
- if (InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())) return;
+ // 获得 Mapper 对应的数据权限的规则
+ List rules = ruleFactory.getDataPermissionRule(ms.getId());
+ if (mappedStatementCache.noRewritable(ms, rules)) { // 如果无需重写,则跳过
+ return;
+ }
+
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
- mpBs.sql(parserMulti(mpBs.sql(), null));
+ try {
+ // 初始化上下文
+ ContextHolder.init(rules);
+ // 处理 SQL
+ mpBs.sql(parserMulti(mpBs.sql(), null));
+ } finally {
+ addMappedStatementCache(ms);
+ ContextHolder.clear();
+ }
}
}
@@ -87,10 +117,6 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
final Table table = update.getTable();
- if (ignoreTable(table.getName())) {
- // 过滤退出执行
- return;
- }
update.setWhere(this.andExpression(table, update.getWhere()));
}
@@ -99,10 +125,6 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
*/
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
- if (ignoreTable(delete.getTable().getName())) {
- // 过滤退出执行
- return;
- }
delete.setWhere(this.andExpression(delete.getTable(), delete.getWhere()));
}
@@ -378,4 +400,116 @@ public class DataPermissionInterceptor extends JsqlParserSupport implements Inne
return new LongValue(1L);
}
+
+ /**
+ * 判断 SQL 是否重写。如果没有重写,则添加到 {@link MappedStatementCache} 中
+ *
+ * @param ms MappedStatement
+ */
+ private void addMappedStatementCache(MappedStatement ms) {
+ if (ContextHolder.getRewrite()) {
+ return;
+ }
+ // 有重写,进行添加
+ mappedStatementCache.addNoRewritable(ms, ContextHolder.getRules());
+ }
+
+ /**
+ * SQL 解析上下文,方便透传 {@link DataPermissionRule} 规则
+ *
+ * @author 芋道源码
+ */
+ private static final class ContextHolder {
+
+ /**
+ * 该 {@link MappedStatement} 对应的规则
+ */
+ private static final ThreadLocal> RULES = new TransmittableThreadLocal<>();
+ /**
+ * SQL 是否进行重写
+ */
+ private static final ThreadLocal REWRITE = new TransmittableThreadLocal<>();
+
+ public static void init(List rules) {
+ RULES.set(rules);
+ REWRITE.set(false);
+ }
+
+ public static void clear() {
+ RULES.remove();
+ REWRITE.remove();
+ }
+
+ public static boolean getRewrite() {
+ return REWRITE.get();
+ }
+
+ public static void setRewrite(boolean rewrite) {
+ REWRITE.set(rewrite);
+ }
+
+ public static List getRules() {
+ return RULES.get();
+ }
+
+ }
+
+ /**
+ * {@link MappedStatement} 缓存
+ * 目前主要用于,记录 {@link DataPermissionRule} 是否对指定 {@link MappedStatement} 无效
+ * 如果无效,则可以避免 SQL 的解析,加快速度
+ *
+ * @author 芋道源码
+ */
+ private static final class MappedStatementCache {
+
+ /**
+ * 无需重写的映射
+ *
+ * value:{@link MappedStatement#getId()} 编号
+ */
+ private final Map, Set> noRewritableMappedStatements = new ConcurrentHashMap<>();
+
+ /**
+ * 判断是否无需重写
+ * ps:虽然有点中文式英语,但是容易读懂即可
+ *
+ * @param ms MappedStatement
+ * @param rules 数据权限规则数组
+ * @return 是否无需重写
+ */
+ public boolean noRewritable(MappedStatement ms, List rules) {
+ // 如果规则为空,说明无需重写
+ if (CollUtil.isEmpty(rules)) {
+ return true;
+ }
+ // 任一规则不在 noRewritableMap 中,则说明可能需要重写
+ for (DataPermissionRule rule : rules) {
+ Set mappedStatementIds = noRewritableMappedStatements.get(rule.getClass());
+ if (!CollUtil.contains(mappedStatementIds, ms.getId())) { // 不存在,则说明可能要重写
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 添加无需重写的 MappedStatement
+ *
+ * @param ms MappedStatement
+ * @param rules 数据权限规则数组
+ */
+ public void addNoRewritable(MappedStatement ms, List rules) {
+ for (DataPermissionRule rule : rules) {
+ Set mappedStatementIds = noRewritableMappedStatements.get(rule.getClass());
+ if (CollUtil.isNotEmpty(mappedStatementIds)) {
+ mappedStatementIds.add(ms.getId());
+ } else {
+ noRewritableMappedStatements.put(rule.getClass(), SetUtils.asSet(ms.getId()));
+ }
+ }
+ }
+
+ }
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactory.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactory.java
index f0191cbc3..166dfea6c 100644
--- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactory.java
+++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/DataPermissionRuleFactory.java
@@ -1,9 +1,28 @@
package cn.iocoder.yudao.framework.datapermission.core.rule;
+import java.util.List;
+
/**
- * {@link DataPermissionRule} 工厂接口,提供如下能力:
- * 1. {@link DataPermissionRule} 的容器
- * 2. TODO 芋艿:
+ * {@link DataPermissionRule} 工厂接口
+ * 作为 {@link DataPermissionRule} 的容器,提供管理能力
+ *
+ * @author 芋道源码
*/
public interface DataPermissionRuleFactory {
+
+ /**
+ * 获得所有数据权限规则数组
+ *
+ * @return 数据权限规则数组
+ */
+ List getDataPermissionRules();
+
+ /**
+ * 获得指定 Mapper 的数据权限规则数组
+ *
+ * @param mappedStatementId 指定 Mapper 的编号
+ * @return 数据权限规则数组
+ */
+ List getDataPermissionRule(String mappedStatementId);
+
}
diff --git a/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest.java b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest.java
index a8b40a05c..86b4ec0df 100644
--- a/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest.java
+++ b/yudao-framework/yudao-spring-boot-starter-data-permission/src/test/java/cn/iocoder/yudao/framework/datapermission/core/interceptor/DataPermissionInterceptorTest.java
@@ -1,12 +1,20 @@
package cn.iocoder.yudao.framework.datapermission.core.interceptor;
+import cn.iocoder.yudao.framework.datapermission.core.rule.DataPermissionRuleFactory;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
import static org.assertj.core.api.Assertions.assertThat;
-public class DataPermissionInterceptorTest {
+public class DataPermissionInterceptorTest extends BaseMockitoUnitTest {
- private final DataPermissionInterceptor interceptor = new DataPermissionInterceptor();
+ @InjectMocks
+ private DataPermissionInterceptor interceptor;
+
+ @Mock
+ private DataPermissionRuleFactory ruleFactory;
@Test
public void selectSingle() {
diff --git a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java
index 53040c6ec..fbc2a2826 100644
--- a/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java
+++ b/yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/core/pubsub/AbstractChannelMessage.java
@@ -15,7 +15,7 @@ public abstract class AbstractChannelMessage extends AbstractRedisMessage {
*
* @return Channel
*/
- @JsonIgnore // 避免序列化
+ @JsonIgnore // 避免序列化。原因是,Redis 发布 Channel 消息的时候,已经会指定。
public abstract String getChannel();
}