This commit is contained in:
XSWL1018 2024-09-10 17:39:57 +08:00
parent e60bdb6407
commit d06fa02df2
21 changed files with 227 additions and 107 deletions

View File

@ -1,48 +0,0 @@
package com.ruoyi.common.context.dataSecurity;
import java.util.List;
import java.util.Map;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.enums.SqlType;
import com.ruoyi.common.model.JoinTableModel;
import com.ruoyi.common.model.WhereModel;
public class DataSecurityContextHolder {
private static final ThreadLocal<JSONObject> DATA_SECURITY_SQL_CONTEXT_HOLDER = new ThreadLocal<>();
public static void startDataSecurity() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("isSecurity", Boolean.TRUE);
jsonObject.put(SqlType.WHERE.getSqlType(), new JSONArray());
jsonObject.put(SqlType.JOIN.getSqlType(), new JSONArray());
DATA_SECURITY_SQL_CONTEXT_HOLDER.set(jsonObject);
}
public static void addWhereParam(WhereModel whereModel) {
DATA_SECURITY_SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.WHERE.getSqlType()).add(whereModel);
}
public static void clearCache() {
DATA_SECURITY_SQL_CONTEXT_HOLDER.remove();
}
public static boolean isSecurity() {
return DATA_SECURITY_SQL_CONTEXT_HOLDER.get() != null
&& DATA_SECURITY_SQL_CONTEXT_HOLDER.get().getBooleanValue("isSecurity");
}
public static JSONArray getWhere() {
return DATA_SECURITY_SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.WHERE.getSqlType());
}
public static void addJoinTable(JoinTableModel joinTableModel) {
DATA_SECURITY_SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.JOIN.getSqlType()).add(joinTableModel);
}
public static JSONArray getJoinTables() {
return DATA_SECURITY_SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.JOIN.getSqlType());
}
}

View File

@ -1,14 +0,0 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.context.dataSecurity.DataSecurityContextHolder;
public class DataSecurityUtil {
public static void closeDataSecurity() {
DataSecurityContextHolder.clearCache();
}
public static void startDataSecurity() {
DataSecurityContextHolder.startDataSecurity();
}
}

View File

@ -44,6 +44,12 @@
<artifactId>ruoyi-middleware-starter</artifactId>
<version>${ruoyi.version}</version>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-midleware-mybatis-interceptor</artifactId>
<version>${ruoyi.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -52,6 +58,7 @@
<module>ruoyi-middleware-minio</module>
<module>ruoyi-middleware-redis</module>
<module>ruoyi-middleware-starter</module>
<module>ruoyi-midleware-mybatis-interceptor</module>
</modules>
<packaging>pom</packaging>
</project>

View File

@ -32,6 +32,11 @@
<artifactId>ruoyi-middleware-redis</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-midleware-mybatis-interceptor</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>ruoyi-middleware</artifactId>
<groupId>com.ruoyi</groupId>
<version>3.8.8.3.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-midleware-mybatis-interceptor</artifactId>
<properties>
<maven.compiler.source>19</maven.compiler.source>
<maven.compiler.target>19</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package annotation;
import com.ruoyi.common.enums.DataSecurityStrategy;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSecurity {
public DataSecurityStrategy strategy() default DataSecurityStrategy.CREEATE_BY;
public String table() default "";
public String joinTableAlise() default "";
}

View File

@ -0,0 +1,10 @@
package annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MybatisHandlerOrder {
public int value() default 0;
}

View File

@ -1,31 +1,26 @@
package com.ruoyi.framework.aspectj;
import java.util.List;
package aspectj;
import context.dataSecurity.SqlContextHolder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.sql.DataSecurity;
import com.ruoyi.common.context.dataSecurity.DataSecurityContextHolder;
import com.ruoyi.common.enums.DataSecurityStrategy;
import com.ruoyi.common.model.JoinTableModel;
import com.ruoyi.common.model.WhereModel;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import ch.qos.logback.core.util.StringUtil;
@Aspect
@Component
public class DataSecurityAspect {
@Before(value = "@annotation(dataSecurity)")
public void doBefore(final JoinPoint point, DataSecurity dataSecurity) throws Throwable {
DataSecurityContextHolder.startDataSecurity();
SqlContextHolder.startDataSecurity();
switch (dataSecurity.strategy()) {
case CREEATE_BY:
WhereModel createByModel = new WhereModel();
@ -34,7 +29,7 @@ public class DataSecurityAspect {
createByModel.setWhereColumn("create_by");
createByModel.setMethod(WhereModel.METHOD_EQUAS);
createByModel.setConnectType(WhereModel.CONNECT_AND);
DataSecurityContextHolder.addWhereParam(createByModel);
SqlContextHolder.addWhereParam(createByModel);
break;
case USER_ID:
WhereModel userIdModel = new WhereModel();
@ -43,7 +38,7 @@ public class DataSecurityAspect {
userIdModel.setValue(SecurityUtils.getUserId());
userIdModel.setConnectType(WhereModel.CONNECT_AND);
userIdModel.setMethod(WhereModel.METHOD_EQUAS);
DataSecurityContextHolder.addWhereParam(userIdModel);
SqlContextHolder.addWhereParam(userIdModel);
break;
case JOINTABLE_CREATE_BY:
JoinTableModel createByTableModel = new JoinTableModel();
@ -56,7 +51,7 @@ public class DataSecurityAspect {
createByTableModel.setFromTableColumn("create_by");
createByTableModel.setJoinTableColumn("user_name");
DataSecurityContextHolder.addJoinTable(createByTableModel);
SqlContextHolder.addJoinTable(createByTableModel);
break;
case JOINTABLE_USER_ID:
JoinTableModel userIdTableModel = new JoinTableModel();
@ -69,7 +64,7 @@ public class DataSecurityAspect {
userIdTableModel.setFromTableColumn("user_id");
userIdTableModel.setJoinTableColumn("user_id");
DataSecurityContextHolder.addJoinTable(userIdTableModel);
SqlContextHolder.addJoinTable(userIdTableModel);
break;
default:
@ -80,6 +75,6 @@ public class DataSecurityAspect {
@After(value = " @annotation(dataSecurity)")
public void doAfter(final JoinPoint point, DataSecurity dataSecurity) {
DataSecurityContextHolder.clearCache();
SqlContextHolder.clearCache();
}
}

View File

@ -0,0 +1,45 @@
package context.dataSecurity;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.enums.SqlType;
import com.ruoyi.common.model.JoinTableModel;
import com.ruoyi.common.model.WhereModel;
public class SqlContextHolder {
private static final ThreadLocal<JSONObject> SQL_CONTEXT_HOLDER = new ThreadLocal<>();
public static void startDataSecurity() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("isSecurity", Boolean.TRUE);
jsonObject.put(SqlType.WHERE.getSqlType(), new JSONArray());
jsonObject.put(SqlType.JOIN.getSqlType(), new JSONArray());
SQL_CONTEXT_HOLDER.set(jsonObject);
}
public static void addWhereParam(WhereModel whereModel) {
SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.WHERE.getSqlType()).add(whereModel);
}
public static void clearCache() {
SQL_CONTEXT_HOLDER.remove();
}
public static boolean isSecurity() {
return SQL_CONTEXT_HOLDER.get() != null
&& SQL_CONTEXT_HOLDER.get().getBooleanValue("isSecurity");
}
public static JSONArray getWhere() {
return SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.WHERE.getSqlType());
}
public static void addJoinTable(JoinTableModel joinTableModel) {
SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.JOIN.getSqlType()).add(joinTableModel);
}
public static JSONArray getJoinTables() {
return SQL_CONTEXT_HOLDER.get().getJSONArray(SqlType.JOIN.getSqlType());
}
}

View File

@ -1,7 +1,7 @@
package com.ruoyi.common.context.page;
package context.page;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.context.page.model.PageInfo;
import context.page.model.PageInfo;
public class PageContextHolder {
private static final ThreadLocal<JSONObject> PAGE_CONTEXT_HOLDER = new ThreadLocal<>();

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.context.page.model;
package context.page.model;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.ServletUtils;

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.context.page.model;
package context.page.model;
import java.util.ArrayList;
import java.util.List;

View File

@ -1,6 +1,5 @@
package com.ruoyi.framework.interceptor.mybatis;
package interceptor.mybatis;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -18,10 +17,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.sql.MybatisHandlerOrder;
import com.ruoyi.common.handler.sql.MybatisAfterHandler;
import com.ruoyi.common.handler.sql.MybatisPreHandler;
import jakarta.annotation.PostConstruct;
import sql.MybatisAfterHandler;
import sql.MybatisPreHandler;
@Component
@Intercepts({

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.handler.sql;
package sql;
public interface MybatisAfterHandler {

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.handler.sql;
package sql;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.handler.sql.dataSecurity;
package sql.dataSecurity;
import java.lang.reflect.Field;
import java.util.List;
@ -13,8 +13,8 @@ import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import com.ruoyi.common.annotation.sql.MybatisHandlerOrder;
import com.ruoyi.common.context.dataSecurity.DataSecurityContextHolder;
import com.ruoyi.common.handler.sql.MybatisPreHandler;
import context.dataSecurity.SqlContextHolder;
import sql.MybatisPreHandler;
import com.ruoyi.common.model.JoinTableModel;
import com.ruoyi.common.model.WhereModel;
import com.ruoyi.common.utils.StringUtils;
@ -44,7 +44,7 @@ public class DataSecurityPreHandler implements MybatisPreHandler {
@Override
public void preHandle(Executor executor, MappedStatement mappedStatement, Object params, RowBounds rowBounds,
ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws Throwable {
if (DataSecurityContextHolder.isSecurity()) {
if (SqlContextHolder.isSecurity()) {
Statement sql = parseSql(SqlUtil.parseSql(boundSql.getSql()));
sqlFiled.set(boundSql, sql.toString());
}
@ -67,10 +67,10 @@ public class DataSecurityPreHandler implements MybatisPreHandler {
Expression expWhere = plain.getWhere();
StringBuilder whereParam = new StringBuilder(" ");
String where = expWhere != null ? expWhere.toString() : null;
if (DataSecurityContextHolder.getWhere() == null || DataSecurityContextHolder.getWhere().size() <= 0) {
if (SqlContextHolder.getWhere() == null || SqlContextHolder.getWhere().size() <= 0) {
return;
}
DataSecurityContextHolder.getWhere().forEach(item -> {
SqlContextHolder.getWhere().forEach(item -> {
whereParam.append(((WhereModel) item).getSqlString());
});
where = StringUtils.isEmpty(where) ? whereParam.toString().substring(5, whereParam.length())
@ -80,10 +80,10 @@ public class DataSecurityPreHandler implements MybatisPreHandler {
private static void handleJoin(Select select) {
PlainSelect selectBody = select.getPlainSelect();
if (DataSecurityContextHolder.getJoinTables() == null || DataSecurityContextHolder.getJoinTables().size() <= 0) {
if (SqlContextHolder.getJoinTables() == null || SqlContextHolder.getJoinTables().size() <= 0) {
return;
}
DataSecurityContextHolder.getJoinTables().forEach(item -> {
SqlContextHolder.getJoinTables().forEach(item -> {
JoinTableModel tableModel = (JoinTableModel) item;
Table table = new Table(tableModel.getJoinTable());
table.setAlias(new Alias(tableModel.getJoinTableAlise()));

View File

@ -1,14 +1,13 @@
package com.ruoyi.common.handler.sql.page;
package sql.page;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.sql.MybatisHandlerOrder;
import com.ruoyi.common.context.page.PageContextHolder;
import com.ruoyi.common.context.page.model.TableInfo;
import com.ruoyi.common.handler.sql.MybatisAfterHandler;
import context.page.PageContextHolder;
import context.page.model.TableInfo;
import sql.MybatisAfterHandler;
@MybatisHandlerOrder(1)
@Component

View File

@ -1,4 +1,4 @@
package com.ruoyi.common.handler.sql.page;
package sql.page;
import java.lang.reflect.Field;
import java.sql.SQLException;
@ -17,9 +17,9 @@ import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;
import com.ruoyi.common.annotation.sql.MybatisHandlerOrder;
import com.ruoyi.common.context.page.PageContextHolder;
import com.ruoyi.common.context.page.model.PageInfo;
import com.ruoyi.common.handler.sql.MybatisPreHandler;
import context.page.PageContextHolder;
import context.page.model.PageInfo;
import sql.MybatisPreHandler;
import com.ruoyi.common.utils.sql.SqlUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.Statement;

View File

@ -0,0 +1,15 @@
package util;
import context.dataSecurity.SqlContextHolder;
public class DataSecurityUtil {
public static void closeDataSecurity() {
SqlContextHolder.clearCache();
}
public static void startDataSecurity() {
SqlContextHolder.startDataSecurity();
}
}

View File

@ -0,0 +1,64 @@
package util;
import com.ruoyi.common.exception.UtilException;
import com.ruoyi.common.utils.StringUtils;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserManager;
import net.sf.jsqlparser.statement.Statement;
import java.io.StringReader;
/**
* sql操作工具类
*
* @author ruoyi
*/
public class SqlUtil {
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "and |extractvalue|updatexml|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |+|user()";
/**
* 仅支持字母数字下划线空格逗号小数点支持多个字段排序
*/
public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
private static final CCJSqlParserManager parserManager = new CCJSqlParserManager();
/**
* 检查字符防止注入绕过
*/
public static String escapeOrderBySql(String value) {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
throw new UtilException("参数不符合规范,不能进行查询");
}
return value;
}
/**
* 验证 order by 语法是否符合规范
*/
public static boolean isValidOrderBySql(String value) {
return value.matches(SQL_PATTERN);
}
/**
* SQL关键字检查
*/
public static void filterKeyword(String value) {
if (StringUtils.isEmpty(value)) {
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new UtilException("参数存在SQL注入风险");
}
}
}
public static Statement parseSql(String sql) throws JSQLParserException {
return parserManager.parse(new StringReader(sql));
}
}