🐎 数据权限性能优化:对于无需数据权限控制的 sql 在解析一次后进行记录,后续不再进行解析处理

This commit is contained in:
b2baccline
2021-09-26 22:27:26 +08:00
parent 858177f2e3
commit d9c1062e29
5 changed files with 118 additions and 11 deletions

View File

@@ -3,9 +3,14 @@ package com.hccake.ballcat.common.datascope.handler;
import com.hccake.ballcat.common.datascope.DataScope;
import com.hccake.ballcat.common.datascope.annotation.DataPermission;
import com.hccake.ballcat.common.datascope.holder.DataPermissionAnnotationHolder;
import com.hccake.ballcat.common.datascope.holder.MappedStatementIdsWithoutDataScope;
import lombok.RequiredArgsConstructor;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -64,13 +69,17 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
}
/**
* 是否忽略权限控制,默认不忽略
* <p>
* 是否忽略权限控制
* </p>
* 若当前的 mappedStatementId 存在于 <Code>MappedStatementIdsWithoutDataScope<Code/>
* 中,则表示无需处理
* @param mappedStatementId Mapper方法ID
* @return always false
*/
@Override
public boolean ignorePermissionControl(String mappedStatementId) {
return false;
return MappedStatementIdsWithoutDataScope.contains(mappedStatementId);
}
}

View File

@@ -0,0 +1,45 @@
package com.hccake.ballcat.common.datascope.holder;
import java.util.concurrent.atomic.AtomicInteger;
/**
* DataScope 匹配数
*
* @author hccake
*/
public final class DataScopeMatchNumHolder {
private DataScopeMatchNumHolder() {
}
private static ThreadLocal<AtomicInteger> matchNumTreadLocal;
public static void create() {
matchNumTreadLocal = ThreadLocal.withInitial(AtomicInteger::new);
}
/**
* get dataScope
* @return dataScopes
*/
public static int getMatchNum() {
AtomicInteger matchNum = matchNumTreadLocal.get();
return matchNum.get();
}
/**
* 添加 dataScope
*/
public static void incrementMatchNum() {
AtomicInteger matchNum = matchNumTreadLocal.get();
matchNum.incrementAndGet();
}
/**
* 删除 dataScope
*/
public static void remove() {
matchNumTreadLocal.remove();
}
}

View File

@@ -0,0 +1,23 @@
package com.hccake.ballcat.common.datascope.holder;
import java.util.HashSet;
import java.util.Set;
/**
* 该类用于存储,不需数据权限处理的 mappedStatementId 集合
*
* @author hccake
*/
public class MappedStatementIdsWithoutDataScope {
private static final Set<String> MAPPED_STATEMENT_IDS = new HashSet<>();
public static void addStatementId(String mappedStatementId) {
MAPPED_STATEMENT_IDS.add(mappedStatementId);
}
public static boolean contains(String mappedStatementId) {
return MAPPED_STATEMENT_IDS.contains(mappedStatementId);
}
}

View File

@@ -2,13 +2,19 @@ package com.hccake.ballcat.common.datascope.interceptor;
import com.hccake.ballcat.common.datascope.DataScope;
import com.hccake.ballcat.common.datascope.handler.DataPermissionHandler;
import com.hccake.ballcat.common.datascope.holder.DataScopeMatchNumHolder;
import com.hccake.ballcat.common.datascope.holder.MappedStatementIdsWithoutDataScope;
import com.hccake.ballcat.common.datascope.processor.DataScopeSqlProcessor;
import com.hccake.ballcat.common.datascope.util.PluginUtils;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import java.sql.Connection;
import java.util.List;
@@ -49,6 +55,9 @@ public class DataPermissionInterceptor implements Interceptor {
return invocation.proceed();
}
try {
// 创建 matchNumTreadLocal
DataScopeMatchNumHolder.create();
// 根据 DataScopes 进行数据权限的 sql 处理
if (sct == SqlCommandType.SELECT) {
mpBs.sql(dataScopeSqlProcessor.parserSingle(mpBs.sql(), dataScopes));
@@ -56,6 +65,14 @@ public class DataPermissionInterceptor implements Interceptor {
else if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
mpBs.sql(dataScopeSqlProcessor.parserMulti(mpBs.sql(), dataScopes));
}
// 如果解析后发现当前 mappedStatementId 对应的 sql没有任何数据权限匹配则记录下来后续可以直接跳过不解析
if (DataScopeMatchNumHolder.getMatchNum() == 0) {
MappedStatementIdsWithoutDataScope.addStatementId(mappedStatementId);
}
}
finally {
DataScopeMatchNumHolder.remove();
}
// 执行 sql
return invocation.proceed();

View File

@@ -2,6 +2,7 @@ package com.hccake.ballcat.common.datascope.processor;
import com.hccake.ballcat.common.datascope.DataScope;
import com.hccake.ballcat.common.datascope.holder.DataScopeHolder;
import com.hccake.ballcat.common.datascope.holder.DataScopeMatchNumHolder;
import com.hccake.ballcat.common.datascope.parser.JsqlParserSupport;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -34,12 +35,14 @@ import net.sf.jsqlparser.statement.select.SubSelect;
import net.sf.jsqlparser.statement.select.ValuesList;
import net.sf.jsqlparser.statement.select.WithItem;
import net.sf.jsqlparser.statement.update.Update;
import org.springframework.util.CollectionUtils;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* 数据权限 sql 处理器 参考 mybatis-plus 租户拦截器,解析 sql where 部分,进行查询表达式注入
@@ -351,8 +354,18 @@ public class DataScopeSqlProcessor extends JsqlParserSupport {
// 获取表名
String tableName = getTableName(table.getName());
List<DataScope> dataScopes = DataScopeHolder.get();
Expression dataFilterExpression = dataScopes.stream().filter(x -> x.getTableNames().contains(tableName))
// 进行 dataScope 的表名匹配
List<DataScope> matchDataScopes = DataScopeHolder.get().stream()
.filter(x -> x.getTableNames().contains(tableName)).collect(Collectors.toList());
if (CollectionUtils.isEmpty(matchDataScopes)) {
return currentExpression;
}
// 匹配则计数
DataScopeMatchNumHolder.incrementMatchNum();
// 获取到数据权限过滤的表达式
Expression dataFilterExpression = matchDataScopes.stream()
.map(x -> x.getExpression(tableName, table.getAlias())).filter(Objects::nonNull)
.reduce(AndExpression::new).orElse(null);