diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java
index 62f61f311..c791cf28d 100644
--- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java
+++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/datascope/DataScopeInnerInterceptor.java
@@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.Statement;
@@ -19,12 +21,18 @@ import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
- * @author legal
+ * 数据权限内部拦截器
+ *
+ * 参照 MyBatis Plus {@code DataPermissionInterceptor} 的实现思路,
+ * 通过 JSqlParser 解析 SQL 为 AST,使用 {@code PlainSelect.setWhere()} 直接在
+ * WHERE 子句上追加数据权限条件,避免子查询包装导致索引失效。
+ * 对于 UNION 等复杂 SQL 或解析失败的情况,回退到子查询包装方式保证兼容性。
+ *
+ * @author lengleng
* @date 2020/11/29
*/
@Slf4j
@@ -62,155 +70,149 @@ public class DataScopeInnerInterceptor implements DataScopeInterceptor {
mpBs.sql(String.format("SELECT %s FROM (%s) temp_data_scope", dataScope.getFunc().getType(), originalSql));
return;
}
- // 分割sql语句
- List where = splitSqlByWhere(originalSql);
- // 获取表名
- String tableName = getTableName(originalSql);
- List deptIds = dataScope.getDeptList();
-
- // 1.无数据权限限制,则直接返回 0 条数据
- if (CollUtil.isEmpty(deptIds) && StrUtil.isBlank(dataScope.getUsername())) {
- if (CollUtil.isNotEmpty(where) && where.size() > 1) {
- originalSql = String.format("%s WHERE 1 = 2 AND %s", where.get(0), where.get(1));
- } else {
- originalSql = String.format("%s WHERE 1 = 2", where.get(0));
- }
- /*originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE 1 = 2",
- dataScope.getFunc().getType(), originalSql);*/
- }
- // 2.如果为本人权限 + 部门权限控制
- else if (StrUtil.isNotBlank(dataScope.getUsername()) && CollUtil.isNotEmpty(deptIds)) {
- String join = CollectionUtil.join(deptIds, ",");
- // 使用配置的组合模式(AND/OR),默认OR保持向后兼容
- String logicKeyword = dataScope.getLogicMode() != null ? dataScope.getLogicMode().getKeyword()
- : DataScopeLogicEnum.OR.getKeyword();
- originalSql = String.format("%s WHERE %s.%s = '%s' %s %s.%s IN (%s)",
- where.get(0),
- tableName,
- dataScope.getScopeUserName(),
- dataScope.getUsername(),
- logicKeyword,
- tableName,
- dataScope.getScopeDeptName(),
- join
- );
- if (CollUtil.isNotEmpty(where) && where.size() > 1) {
- originalSql = originalSql + " AND " + where.get(1);
- }
- /*originalSql = String.format(
- "SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s' %s temp_data_scope.%s IN (%s)",
- dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername(),
- logicKeyword, dataScope.getScopeDeptName(), join);*/
- }
- // 3. 如果为本人
- else if (StrUtil.isNotBlank(dataScope.getUsername())) {
- if (CollUtil.isNotEmpty(where) && where.size() > 1) {
- originalSql = String.format("%s WHERE %s.%s = '%s' AND %s",
- where.get(0),
- tableName,
- dataScope.getScopeUserName(),
- dataScope.getUsername(),
- where.get(1)
- );
- } else {
- originalSql = String.format("%s WHERE %s.%s = '%s'",
- where.get(0),
- tableName,
- dataScope.getScopeUserName(),
- dataScope.getUsername()
- );
- }
- /*originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s'",
- dataScope.getFunc().getType(), originalSql, dataScope.getScopeUserName(), dataScope.getUsername());*/
- }
- // 4.部门权限控制
- else {
- String join = CollectionUtil.join(deptIds, ",");
- if (CollUtil.isNotEmpty(where) && where.size() > 1) {
- originalSql = String.format("%s WHERE %s.%s IN (%s) AND %s",
- where.get(0),
- tableName,
- dataScope.getScopeDeptName(),
- join,
- where.get(1)
- );
- } else {
- originalSql = String.format("%s WHERE %s.%s IN (%s)",
- where.get(0),
- tableName,
- dataScope.getScopeDeptName(),
- join
- );
- }
- /*originalSql = String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s IN (%s)",
- dataScope.getFunc().getType(), originalSql, dataScope.getScopeDeptName(), join);*/
- }
-
- mpBs.sql(originalSql);
- }
-
- /**
- * 获取表名(优先取别名,其次取原表名)
- */
- private static String getTableName(String originalSql) {
- if (StrUtil.isBlank(originalSql)) {
- return "";
- }
try {
Statement statement = CCJSqlParserUtil.parse(originalSql);
if (!(statement instanceof Select selectStatement)) {
- return "";
+ return;
}
- // 增加 PlainSelect 类型判断,避免 UNION 等复杂语句强转异常
+ // UNION 等复杂语句无法直接操作 WHERE,回退到子查询包装
if (!(selectStatement.getSelectBody() instanceof PlainSelect plainSelect)) {
- return "";
+ mpBs.sql(buildSubQuerySql(originalSql, dataScope));
+ return;
}
- FromItem fromItem = plainSelect.getFromItem();
- if (fromItem == null) {
- return "";
+ // 构建数据权限条件表达式
+ Expression scopeExpression = buildScopeExpression(plainSelect, dataScope);
+ if (scopeExpression == null) {
+ return;
}
- // 优先使用别名
- if (fromItem.getAlias() != null) {
- return fromItem.getAlias().getName();
- }
-
- // 其次使用原表名
- if (fromItem instanceof Table table) {
- return table.getName();
+ // 与原有 WHERE 条件合并(数据权限条件 AND 原条件)
+ Expression existingWhere = plainSelect.getWhere();
+ if (existingWhere != null) {
+ plainSelect.setWhere(new AndExpression(scopeExpression, existingWhere));
+ } else {
+ plainSelect.setWhere(scopeExpression);
}
+ mpBs.sql(selectStatement.toString());
} catch (JSQLParserException e) {
- log.error("JSQLParserException: {} -- sql: {}", e.getMessage(), originalSql);
+ log.warn("数据权限 SQL 解析失败,回退到子查询包装: {}", originalSql, e);
+ mpBs.sql(buildSubQuerySql(originalSql, dataScope));
}
- return "";
}
/**
- * 分割SQL by WHERE
- * @return 分割结果,失败返回空集合
+ * 根据 DataScope 配置构建数据权限条件表达式
+ *
+ * 使用 {@code CCJSqlParserUtil.parseCondExpression()} 解析条件字符串为 Expression 对象,
+ * 与 MyBatis Plus DataPermissionInterceptor 保持一致的实现方式。
+ *
+ * @param plainSelect 解析后的 PlainSelect 对象
+ * @param dataScope 数据权限参数
+ * @return 数据权限条件表达式,无需处理时返回 null
*/
- private static List splitSqlByWhere(String sql) {
- if (StrUtil.isBlank(sql)) {
- return Collections.emptyList();
+ private Expression buildScopeExpression(PlainSelect plainSelect, DataScope dataScope)
+ throws JSQLParserException {
+ List deptIds = dataScope.getDeptList();
+ String username = dataScope.getUsername();
+
+ // 获取表引用(优先别名,其次表名),用于限定列名
+ String tableRef = getTableReference(plainSelect.getFromItem());
+ String prefix = StrUtil.isNotBlank(tableRef) ? tableRef + "." : "";
+
+ // 1. 无数据权限限制,返回 0 条数据
+ if (CollUtil.isEmpty(deptIds) && StrUtil.isBlank(username)) {
+ return CCJSqlParserUtil.parseCondExpression("1 = 2");
}
- try {
- // 统一转大写匹配,避免遗漏大小写混合情况(如 Where / wHere)
- String upperSql = sql.toUpperCase();
- int index = upperSql.indexOf(" WHERE ");
- if (index != -1) {
- return List.of(
- sql.substring(0, index),
- sql.substring(index + " WHERE ".length())
- );
- }
- } catch (Exception e) {
- log.error("分割SQL异常:{}--:{}", e, sql);
+
+ // 2. 本人权限 + 部门权限:加括号保证与原有条件组合时的优先级
+ if (StrUtil.isNotBlank(username) && CollUtil.isNotEmpty(deptIds)) {
+ String join = CollectionUtil.join(deptIds, ",");
+ String logicKeyword = dataScope.getLogicMode() != null ? dataScope.getLogicMode().getKeyword()
+ : DataScopeLogicEnum.OR.getKeyword();
+ String condSql = String.format("(%s%s = '%s' %s %s%s IN (%s))", prefix, dataScope.getScopeUserName(),
+ escapeSql(username), logicKeyword, prefix, dataScope.getScopeDeptName(), join);
+ return CCJSqlParserUtil.parseCondExpression(condSql);
}
- return Collections.emptyList();
+
+ // 3. 仅本人权限
+ if (StrUtil.isNotBlank(username)) {
+ String condSql = String.format("%s%s = '%s'", prefix, dataScope.getScopeUserName(), escapeSql(username));
+ return CCJSqlParserUtil.parseCondExpression(condSql);
+ }
+
+ // 4. 仅部门权限
+ String join = CollectionUtil.join(deptIds, ",");
+ String condSql = String.format("%s%s IN (%s)", prefix, dataScope.getScopeDeptName(), join);
+ return CCJSqlParserUtil.parseCondExpression(condSql);
+ }
+
+ /**
+ * 获取表引用名称(优先别名,其次表名)
+ *
+ * @param fromItem FROM 子句项
+ * @return 表引用名称,无法获取时返回 null
+ */
+ private String getTableReference(FromItem fromItem) {
+ if (fromItem == null) {
+ return null;
+ }
+ if (fromItem.getAlias() != null) {
+ return fromItem.getAlias().getName();
+ }
+ if (fromItem instanceof Table table) {
+ return table.getName();
+ }
+ return null;
+ }
+
+ /**
+ * 回退方案:子查询包装(用于 UNION 等复杂 SQL 或解析失败时)
+ */
+ private String buildSubQuerySql(String originalSql, DataScope dataScope) {
+ List deptIds = dataScope.getDeptList();
+ String username = dataScope.getUsername();
+ String selectType = dataScope.getFunc().getType();
+
+ // 无权限
+ if (CollUtil.isEmpty(deptIds) && StrUtil.isBlank(username)) {
+ return String.format("SELECT %s FROM (%s) temp_data_scope WHERE 1 = 2", selectType, originalSql);
+ }
+
+ // 本人 + 部门
+ if (StrUtil.isNotBlank(username) && CollUtil.isNotEmpty(deptIds)) {
+ String join = CollectionUtil.join(deptIds, ",");
+ String logicKeyword = dataScope.getLogicMode() != null ? dataScope.getLogicMode().getKeyword()
+ : DataScopeLogicEnum.OR.getKeyword();
+ return String.format(
+ "SELECT %s FROM (%s) temp_data_scope WHERE (temp_data_scope.%s = '%s' %s temp_data_scope.%s IN (%s))",
+ selectType, originalSql, dataScope.getScopeUserName(), escapeSql(username), logicKeyword,
+ dataScope.getScopeDeptName(), join);
+ }
+
+ // 仅本人
+ if (StrUtil.isNotBlank(username)) {
+ return String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s = '%s'", selectType,
+ originalSql, dataScope.getScopeUserName(), escapeSql(username));
+ }
+
+ // 仅部门
+ String join = CollectionUtil.join(deptIds, ",");
+ return String.format("SELECT %s FROM (%s) temp_data_scope WHERE temp_data_scope.%s IN (%s)", selectType,
+ originalSql, dataScope.getScopeDeptName(), join);
+ }
+
+ /**
+ * 转义 SQL 字符串值中的单引号,防止 SQL 注入
+ */
+ private String escapeSql(String value) {
+ if (value == null) {
+ return "";
+ }
+ return value.replace("'", "''");
}
/**
@@ -225,11 +227,11 @@ public class DataScopeInnerInterceptor implements DataScopeInterceptor {
} else if (parameterObj instanceof Map) {
for (Object val : ((Map, ?>) parameterObj).values()) {
if (val instanceof DataScope) {
- return (DataScope) val;
- }
- }
- }
- return null;
- }
+ return (DataScope) val;
+ }
+ }
+ }
+ return null;
+ }
-}
\ No newline at end of file
+}