diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml index 4da6e212f4ad75d61816054abd04127e195268f5..57f87fcfe298b77dc99f8258f87282fc1fc2c8c6 100644 --- a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -16,5 +16,9 @@ PUBLIC "-//mybatis.org//DTD Config 3.0//EN" - + + + + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java index 2650fb7b2739be941f8c142d3fbdd37997bc404f..36b5489ee387d2f9a81aff5093b734ca4a422096 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -5,7 +5,7 @@ import com.ruoyi.common.utils.StringUtils; /** * sql操作工具类 - * + * * @author ruoyi */ public class SqlUtil diff --git a/ruoyi-middleware/pom.xml b/ruoyi-middleware/pom.xml index 20f21403c8f57123f658cab69af8cdc12257821e..c6895ebe23ca6689af32cf1ebdce0e93a3ac2cd2 100644 --- a/ruoyi-middleware/pom.xml +++ b/ruoyi-middleware/pom.xml @@ -44,6 +44,7 @@ ruoyi-middleware-starter ${ruoyi.version} + @@ -52,6 +53,7 @@ ruoyi-middleware-minio ruoyi-middleware-redis ruoyi-middleware-starter + pom - \ No newline at end of file + diff --git a/ruoyi-middleware/ruoyi-middleware-starter/pom.xml b/ruoyi-middleware/ruoyi-middleware-starter/pom.xml index 870ca1ecf23a33ec3f355423fedf55c6cf6e8c82..bb7eca4fa1ac4242cc262e4b071beec58fab7902 100644 --- a/ruoyi-middleware/ruoyi-middleware-starter/pom.xml +++ b/ruoyi-middleware/ruoyi-middleware-starter/pom.xml @@ -32,6 +32,7 @@ ruoyi-middleware-redis + diff --git a/ruoyi-plugins/pom.xml b/ruoyi-plugins/pom.xml index 837a95a0b28640a969af2119c55f6375a1fb0a0f..f956740e6e8f214b7315c61c44e2b405ab40bdcd 100644 --- a/ruoyi-plugins/pom.xml +++ b/ruoyi-plugins/pom.xml @@ -80,6 +80,12 @@ ruoyi-plugins-starter ${ruoyi.version} + + com.ruoyi + ruoyi-mybatis-interceptor + ${ruoyi.version} + + @@ -90,6 +96,7 @@ ruoyi-mybatis-plus ruoyi-websocket ruoyi-plugins-starter + ruoyi-mybatis-interceptor pom - \ No newline at end of file + diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/pom.xml b/ruoyi-plugins/ruoyi-mybatis-interceptor/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..5d1386233fa727e73da96b5d19baa9e12bad9d8a --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/pom.xml @@ -0,0 +1,31 @@ + + + + ruoyi-plugins + com.ruoyi + 3.8.8.3.1 + + 4.0.0 + + ruoyi-mybatis-interceptor + + + 19 + 19 + UTF-8 + + + + com.ruoyi + ruoyi-common + + + + org.springframework.boot + spring-boot-starter-aop + + + + diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/DataSecurity.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/DataSecurity.java new file mode 100644 index 0000000000000000000000000000000000000000..ae9f8e3f1e1a3e5fc7dfebc5fd5dd3b31b2e8d4b --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/DataSecurity.java @@ -0,0 +1,16 @@ +package com.ruoyi.mybatisinterceptor.annotation; + +import com.ruoyi.mybatisinterceptor.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 ""; +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/MybatisHandlerOrder.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/MybatisHandlerOrder.java new file mode 100644 index 0000000000000000000000000000000000000000..e897354004559113a020562b7253ce824677bc54 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/annotation/MybatisHandlerOrder.java @@ -0,0 +1,10 @@ +package com.ruoyi.mybatisinterceptor.annotation; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface MybatisHandlerOrder { + public int value() default 0; +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java new file mode 100644 index 0000000000000000000000000000000000000000..166b87fc862e52cd155bcf8a41120d91f860fd5f --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/aspectj/DataSecurityAspect.java @@ -0,0 +1,82 @@ +package com.ruoyi.mybatisinterceptor.aspectj; + +import com.ruoyi.mybatisinterceptor.annotation.DataSecurity; +import com.ruoyi.mybatisinterceptor.model.JoinTableModel; +import com.ruoyi.mybatisinterceptor.model.WhereModel; +import com.ruoyi.mybatisinterceptor.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.springframework.stereotype.Component; + + + +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; + + +@Aspect +@Component +public class DataSecurityAspect { + + @Before(value = "@annotation(dataSecurity)") + public void doBefore(final JoinPoint point, DataSecurity dataSecurity) throws Throwable { + SqlContextHolder.startDataSecurity(); + switch (dataSecurity.strategy()) { + case CREEATE_BY: + WhereModel createByModel = new WhereModel(); + createByModel.setTable(dataSecurity.table()); + createByModel.setValue("\"" + SecurityUtils.getUsername() + "\""); + createByModel.setWhereColumn("create_by"); + createByModel.setMethod(WhereModel.METHOD_EQUAS); + createByModel.setConnectType(WhereModel.CONNECT_AND); + SqlContextHolder.addWhereParam(createByModel); + break; + case USER_ID: + WhereModel userIdModel = new WhereModel(); + userIdModel.setTable(dataSecurity.table()); + userIdModel.setTable("user_id"); + userIdModel.setValue(SecurityUtils.getUserId()); + userIdModel.setConnectType(WhereModel.CONNECT_AND); + userIdModel.setMethod(WhereModel.METHOD_EQUAS); + SqlContextHolder.addWhereParam(userIdModel); + break; + case JOINTABLE_CREATE_BY: + JoinTableModel createByTableModel = new JoinTableModel(); + createByTableModel.setFromTable(dataSecurity.table()); + createByTableModel.setFromTableAlise(dataSecurity.table()); + createByTableModel.setJoinTable("sys_user"); + if (!StringUtils.isEmpty(dataSecurity.joinTableAlise())) { + createByTableModel.setJoinTableAlise(dataSecurity.joinTableAlise()); + } + + createByTableModel.setFromTableColumn("create_by"); + createByTableModel.setJoinTableColumn("user_name"); + SqlContextHolder.addJoinTable(createByTableModel); + break; + case JOINTABLE_USER_ID: + JoinTableModel userIdTableModel = new JoinTableModel(); + userIdTableModel.setFromTable(dataSecurity.table()); + userIdTableModel.setFromTableAlise(dataSecurity.table()); + userIdTableModel.setJoinTable("sys_user"); + if (!StringUtils.isEmpty(dataSecurity.joinTableAlise())) { + userIdTableModel.setJoinTableAlise(dataSecurity.joinTableAlise()); + } + + userIdTableModel.setFromTableColumn("user_id"); + userIdTableModel.setJoinTableColumn("user_id"); + SqlContextHolder.addJoinTable(userIdTableModel); + break; + + default: + break; + } + + } + + @After(value = " @annotation(dataSecurity)") + public void doAfter(final JoinPoint point, DataSecurity dataSecurity) { + SqlContextHolder.clearCache(); + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..54407a68984526e28be6c5cb2660cf9643223afa --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/dataSecurity/SqlContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.mybatisinterceptor.context.dataSecurity; + +import com.alibaba.fastjson2.JSONArray; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.mybatisinterceptor.enums.SqlType; +import com.ruoyi.mybatisinterceptor.model.JoinTableModel; +import com.ruoyi.mybatisinterceptor.model.WhereModel; + +public class SqlContextHolder { + private static final ThreadLocal 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()); + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/PageContextHolder.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/PageContextHolder.java new file mode 100644 index 0000000000000000000000000000000000000000..4eef6861ff5bb0a2886547730422ede060f4db16 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/PageContextHolder.java @@ -0,0 +1,44 @@ +package com.ruoyi.mybatisinterceptor.context.page; + +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.mybatisinterceptor.context.page.model.PageInfo; + +public class PageContextHolder { + private static final ThreadLocal PAGE_CONTEXT_HOLDER = new ThreadLocal<>(); + + private static final String PAGE_FLAG = "isPage"; + + private static final String PAGE_INFO = "pageInfo"; + + private static final String TOTAL = "total"; + + public static void startPage() { + JSONObject jsonObject = new JSONObject(); + jsonObject.put(PAGE_FLAG, Boolean.TRUE); + PAGE_CONTEXT_HOLDER.set(jsonObject); + } + + public static void setPageInfo() { + PAGE_CONTEXT_HOLDER.get().put(PAGE_INFO, PageInfo.defaultPageInfo()); + } + + public static PageInfo getPageInfo() { + return (PageInfo) PAGE_CONTEXT_HOLDER.get().get(PAGE_INFO); + } + + public static void clear() { + PAGE_CONTEXT_HOLDER.remove(); + } + + public static boolean isPage() { + return PAGE_CONTEXT_HOLDER.get() != null && PAGE_CONTEXT_HOLDER.get().getBooleanValue(PAGE_FLAG); + } + + public static void setTotal(Long total) { + PAGE_CONTEXT_HOLDER.get().put(TOTAL, total); + } + + public static Long getTotal() { + return PAGE_CONTEXT_HOLDER.get().getLong(TOTAL); + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/PageInfo.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/PageInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..e0cd7a04763a3e9562720fa62232a86997846b45 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/PageInfo.java @@ -0,0 +1,63 @@ +package com.ruoyi.mybatisinterceptor.context.page.model; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +public class PageInfo { + + private Long pageNumber; + + private Long pageSize; + + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + public Long getPageNumber() { + return pageNumber; + } + + public void setPageNumber(Long pageNumber) { + this.pageNumber = pageNumber; + } + + public Long getPageSize() { + return pageSize; + } + + public void setPageSize(Long pageSize) { + this.pageSize = pageSize; + } + + public static PageInfo defaultPageInfo() { + PageInfo pageInfo = new PageInfo(); + pageInfo.setPageNumber(Long.valueOf(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1))); + pageInfo.setPageSize(Long.valueOf(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10))); + return pageInfo; + } + + public Long getOffeset() { + return (pageNumber.longValue() - 1L) * pageSize; + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/RuoyiTableData.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/RuoyiTableData.java new file mode 100644 index 0000000000000000000000000000000000000000..925c1fe5a33355c90e9ee1361230461f9ca992e7 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/RuoyiTableData.java @@ -0,0 +1,25 @@ +package com.ruoyi.mybatisinterceptor.context.page.model; + +import java.util.List; + +public class RuoyiTableData { + private Long total; + private List data; + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/TableInfo.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/TableInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..b055a58f92b0d35017e6d903e0aeeefa715a3f63 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/context/page/model/TableInfo.java @@ -0,0 +1,22 @@ +package com.ruoyi.mybatisinterceptor.context.page.model; + +import java.util.ArrayList; +import java.util.List; + +public class TableInfo extends ArrayList { + + private Long total; + + public TableInfo(List list) { + super(list); + } + + public Long getTotal() { + return total; + } + + public void setTotal(Long total) { + this.total = total; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/DataSecurityStrategy.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/DataSecurityStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..16358c19ad143e7a9a817be81f53b305168d23be --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/DataSecurityStrategy.java @@ -0,0 +1,8 @@ +package com.ruoyi.mybatisinterceptor.enums; + +public enum DataSecurityStrategy { + JOINTABLE_CREATE_BY, + JOINTABLE_USER_ID, + CREEATE_BY, + USER_ID; +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/SqlType.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/SqlType.java new file mode 100644 index 0000000000000000000000000000000000000000..52af7d4c5a1c83821a629196268aa19502176143 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/enums/SqlType.java @@ -0,0 +1,18 @@ +package com.ruoyi.mybatisinterceptor.enums; + +public enum SqlType { + WHERE("where"), + JOIN("join"), + SELECT("select"), + LIMIT("limit"); + + private String sqlType; + + public String getSqlType() { + return sqlType; + } + + private SqlType(String sqlType) { + this.sqlType = sqlType; + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..179e1508846f80d6c75908e1fc13149d282ac1cf --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/interceptor/mybatis/MybatisInterceptor.java @@ -0,0 +1,129 @@ +package com.ruoyi.mybatisinterceptor.interceptor.mybatis; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; +import com.ruoyi.mybatisinterceptor.sql.MybatisAfterHandler; +import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + +import jakarta.annotation.PostConstruct; + +@Component +@Intercepts({ + @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, + RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }), + @Signature(type = Executor.class, method = "query", args = { + MappedStatement.class, Object.class, RowBounds.class, + ResultHandler.class }) + +}) +public class MybatisInterceptor implements Interceptor { + + @Autowired + private List preHandlerBeans; + + @Autowired + private List afterHandlerBeans; + + private static List preHandlersChain; + + private static List afterHandlersChain; + + @PostConstruct + public void init() { + List sortedPreHandlers = preHandlerBeans.stream().sorted((item1, item2) -> { + int a; + int b; + MybatisHandlerOrder ann1 = item1.getClass().getAnnotation(MybatisHandlerOrder.class); + MybatisHandlerOrder ann2 = item2.getClass().getAnnotation(MybatisHandlerOrder.class); + if (ann1 == null) { + a = 0; + } else { + a = ann1.value(); + } + if (ann2 == null) { + b = 0; + } else { + b = ann2.value(); + } + return a - b; + }).collect(Collectors.toList()); + preHandlersChain = sortedPreHandlers; + + List sortedAfterHandlers = afterHandlerBeans.stream().sorted((item1, item2) -> { + int a; + int b; + MybatisHandlerOrder ann1 = item1.getClass().getAnnotation(MybatisHandlerOrder.class); + MybatisHandlerOrder ann2 = item2.getClass().getAnnotation(MybatisHandlerOrder.class); + if (ann1 == null) { + a = 0; + } else { + a = ann1.value(); + } + if (ann2 == null) { + b = 0; + } else { + b = ann2.value(); + } + return a - b; + }).collect(Collectors.toList()); + afterHandlersChain = sortedAfterHandlers; + } + + @Override + public Object intercept(Invocation invocation) throws Throwable { + Executor targetExecutor = (Executor) invocation.getTarget(); + Object[] args = invocation.getArgs(); + if (args.length < 6) { + if (preHandlersChain != null && preHandlersChain.size() > 0) { + MappedStatement ms = (MappedStatement) args[0]; + Object parameterObject = args[1]; + RowBounds rowBounds = (RowBounds) args[2]; + Executor executor = (Executor) invocation.getTarget(); + BoundSql boundSql = ms.getBoundSql(parameterObject); + // 可以对参数做各种处理 + CacheKey cacheKey = executor.createCacheKey(ms, parameterObject, rowBounds, boundSql); + for (MybatisPreHandler item : preHandlersChain) { + item.preHandle(targetExecutor, ms, args[1], (RowBounds) args[2], + (ResultHandler) args[3], cacheKey, boundSql); + } + } + Object result = invocation.proceed(); + if (afterHandlersChain != null && afterHandlersChain.size() > 0) { + for (MybatisAfterHandler item : afterHandlersChain) { + item.handleObject(result); + } + } + return result; + } + if (preHandlersChain != null && preHandlersChain.size() > 0) { + for (MybatisPreHandler item : preHandlersChain) { + item.preHandle(targetExecutor, (MappedStatement) args[0], args[1], (RowBounds) args[2], + (ResultHandler) args[3], (CacheKey) args[4], (BoundSql) args[5]); + } + } + Object result = invocation.proceed(); + if (afterHandlersChain != null && afterHandlersChain.size() > 0) { + for (MybatisAfterHandler item : afterHandlersChain) { + result = item.handleObject(result); + } + } + return result; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/JoinTableModel.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/JoinTableModel.java new file mode 100644 index 0000000000000000000000000000000000000000..50bd25d1d3ac6a10135745b387e7e6b64d2884ab --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/JoinTableModel.java @@ -0,0 +1,85 @@ +package com.ruoyi.mybatisinterceptor.model; + +import com.ruoyi.common.utils.StringUtils; + +public class JoinTableModel { + private String joinTable; + + private String joinTableAlise; + + private String fromTable; + + private String fromTableAlise; + + private String joinTableColumn; + + private String fromTableColumn; + + public String getJoinTable() { + return joinTable; + } + + public void setJoinTable(String joinTable) { + this.joinTable = joinTable; + } + + public String getJoinTableAlise() { + if (StringUtils.isEmpty(this.joinTableAlise)) { + return this.joinTable; + } + return joinTableAlise; + } + + public void setJoinTableAlise(String joinTableAlise) { + + this.joinTableAlise = joinTableAlise; + } + + public String getFromTable() { + return fromTable; + } + + public void setFromTable(String fromTable) { + this.fromTable = fromTable; + } + + public String getFromTableAlise() { + if (StringUtils.isEmpty(this.fromTableAlise)) { + return this.fromTable; + } + return fromTableAlise; + } + + public void setFromTableAlise(String fromTableAlise) { + this.fromTableAlise = fromTableAlise; + } + + public String getJoinTableColumn() { + + return joinTableColumn; + } + + public void setJoinTableColumn(String joinTableColumn) { + this.joinTableColumn = joinTableColumn; + } + + public String getFromTableColumn() { + return fromTableColumn; + } + + public void setFromTableColumn(String fromTableColumn) { + this.fromTableColumn = fromTableColumn; + } + + public String getJoinTableColumnString() { + return this.getJoinTableAlise() + "." + this.joinTableColumn; + } + + public String getFromTableColumnString() { + if (StringUtils.isEmpty(this.getFromTableAlise())) { + return this.fromTableColumn; + } + return this.getFromTableAlise() + "." + this.fromTableColumn; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/WhereModel.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/WhereModel.java new file mode 100644 index 0000000000000000000000000000000000000000..6456fb59b430be578f698219849a3ac9a73c46a8 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/model/WhereModel.java @@ -0,0 +1,67 @@ +package com.ruoyi.mybatisinterceptor.model; + +import com.ruoyi.common.utils.StringUtils; + +public class WhereModel { + private String whereColumn; + private String table; + private Object value; + private String connectType; + private String method; + + public static final String METHOD_EQUAS = "="; + public static final String METHOD_LIKE = "like"; + public static final String CONNECT_AND = "AND"; + public static final String CONNECT_OR = "OR"; + + public String getWhereColumn() { + return whereColumn; + } + + public void setWhereColumn(String whereColumn) { + this.whereColumn = whereColumn; + } + + public String getTable() { + return table; + } + + public void setTable(String table) { + this.table = table; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public String getFullTableColumn() { + if (StringUtils.isEmpty(this.table)) { + return this.whereColumn; + } + return this.table + "." + this.whereColumn; + } + + public String getConnectType() { + return connectType; + } + + public void setConnectType(String connectType) { + this.connectType = connectType; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getSqlString() { + return String.format(" %s %s %s %s ", this.getConnectType(), this.getFullTableColumn(), this.method, this.value); + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/mybatis/MybatisInterceptor.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/mybatis/MybatisInterceptor.java new file mode 100644 index 0000000000000000000000000000000000000000..ae7d0a3767640f3c0e2c3917bc2a2a0eb8596d8b --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/mybatis/MybatisInterceptor.java @@ -0,0 +1,129 @@ +package com.ruoyi.mybatisinterceptor.mybatis; + +import java.util.List; +import java.util.stream.Collectors; + +import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; +import com.ruoyi.mybatisinterceptor.sql.MybatisAfterHandler; +import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + + + +import jakarta.annotation.PostConstruct; + +@Component +@Intercepts({ + @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, + RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }), + @Signature(type = Executor.class, method = "query", args = { + MappedStatement.class, Object.class, RowBounds.class, + ResultHandler.class }) + +}) +public class MybatisInterceptor implements Interceptor { + + @Autowired + private List preHandlerBeans; + + @Autowired + private List afterHandlerBeans; + + private static List preHandlersChain; + + private static List afterHandlersChain; + + @PostConstruct + public void init() { + List sortedPreHandlers = preHandlerBeans.stream().sorted((item1, item2) -> { + int a; + int b; + MybatisHandlerOrder ann1 = item1.getClass().getAnnotation(MybatisHandlerOrder.class); + MybatisHandlerOrder ann2 = item2.getClass().getAnnotation(MybatisHandlerOrder.class); + if (ann1 == null) { + a = 0; + } else { + a = ann1.value(); + } + if (ann2 == null) { + b = 0; + } else { + b = ann2.value(); + } + return a - b; + }).collect(Collectors.toList()); + preHandlersChain = sortedPreHandlers; + + List sortedAfterHandlers = afterHandlerBeans.stream().sorted((item1, item2) -> { + int a; + int b; + MybatisHandlerOrder ann1 = item1.getClass().getAnnotation(MybatisHandlerOrder.class); + MybatisHandlerOrder ann2 = item2.getClass().getAnnotation(MybatisHandlerOrder.class); + if (ann1 == null) { + a = 0; + } else { + a = ann1.value(); + } + if (ann2 == null) { + b = 0; + } else { + b = ann2.value(); + } + return a - b; + }).collect(Collectors.toList()); + afterHandlersChain = sortedAfterHandlers; + } + + @Override + public Object intercept(Invocation invocation) throws Throwable { + Executor targetExecutor = (Executor) invocation.getTarget(); + Object[] args = invocation.getArgs(); + if (args.length < 6) { + if (preHandlersChain != null && preHandlersChain.size() > 0) { + MappedStatement ms = (MappedStatement) args[0]; + Object parameterObject = args[1]; + RowBounds rowBounds = (RowBounds) args[2]; + Executor executor = (Executor) invocation.getTarget(); + BoundSql boundSql = ms.getBoundSql(parameterObject); + // 可以对参数做各种处理 + CacheKey cacheKey = executor.createCacheKey(ms, parameterObject, rowBounds, boundSql); + for (MybatisPreHandler item : preHandlersChain) { + item.preHandle(targetExecutor, ms, args[1], (RowBounds) args[2], + (ResultHandler) args[3], cacheKey, boundSql); + } + } + Object result = invocation.proceed(); + if (afterHandlersChain != null && afterHandlersChain.size() > 0) { + for (MybatisAfterHandler item : afterHandlersChain) { + item.handleObject(result); + } + } + return result; + } + if (preHandlersChain != null && preHandlersChain.size() > 0) { + for (MybatisPreHandler item : preHandlersChain) { + item.preHandle(targetExecutor, (MappedStatement) args[0], args[1], (RowBounds) args[2], + (ResultHandler) args[3], (CacheKey) args[4], (BoundSql) args[5]); + } + } + Object result = invocation.proceed(); + if (afterHandlersChain != null && afterHandlersChain.size() > 0) { + for (MybatisAfterHandler item : afterHandlersChain) { + result = item.handleObject(result); + } + } + return result; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..133f1116f80b5a5371836b6a56a31ec897712b6a --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisAfterHandler.java @@ -0,0 +1,7 @@ +package com.ruoyi.mybatisinterceptor.sql; + +public interface MybatisAfterHandler { + + Object handleObject(Object object) throws Throwable; + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..5e9e2ebb7f360269feba8f1aae7affade046e4f1 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/MybatisPreHandler.java @@ -0,0 +1,15 @@ +package com.ruoyi.mybatisinterceptor.sql; + +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +public interface MybatisPreHandler { + + void preHandle(Executor executor, MappedStatement mappedStatement, Object params, + RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) + throws Throwable; +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..01a57667ea8f50e036e90cb3268b76dfb31a7548 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/dataSecurity/DataSecurityPreHandler.java @@ -0,0 +1,98 @@ +package com.ruoyi.mybatisinterceptor.sql.dataSecurity; + +import java.lang.reflect.Field; +import java.util.List; + +import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; +import com.ruoyi.mybatisinterceptor.context.dataSecurity.SqlContextHolder; +import com.ruoyi.mybatisinterceptor.model.JoinTableModel; +import com.ruoyi.mybatisinterceptor.model.WhereModel; +import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import net.sf.jsqlparser.JSQLParserException; +import net.sf.jsqlparser.expression.Alias; +import net.sf.jsqlparser.expression.Expression; +import net.sf.jsqlparser.expression.operators.relational.EqualsTo; +import net.sf.jsqlparser.parser.CCJSqlParserUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.schema.Table; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.Join; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; + +@MybatisHandlerOrder(1) +@Component +public class DataSecurityPreHandler implements MybatisPreHandler { + + private static final Field sqlFiled = ReflectionUtils.findField(BoundSql.class, "sql"); + static { + sqlFiled.setAccessible(true); + } + + @Override + public void preHandle(Executor executor, MappedStatement mappedStatement, Object params, RowBounds rowBounds, + ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws Throwable { + if (SqlContextHolder.isSecurity()) { + Statement sql = parseSql(SqlUtil.parseSql(boundSql.getSql())); + sqlFiled.set(boundSql, sql.toString()); + } + } + + private static Statement parseSql(Statement statement) throws JSQLParserException { + if (statement instanceof Select) { + Select select = (Select) statement; + // plain.setWhere(CCJSqlParserUtil.parseCondExpression(handleWhere(expWhere))); + handleWhere(select); + handleJoin(select); + return select; + } else { + return statement; + } + } + + private static void handleWhere(Select select) throws JSQLParserException { + PlainSelect plain = select.getPlainSelect(); + Expression expWhere = plain.getWhere(); + StringBuilder whereParam = new StringBuilder(" "); + String where = expWhere != null ? expWhere.toString() : null; + if (SqlContextHolder.getWhere() == null || SqlContextHolder.getWhere().size() <= 0) { + return; + } + SqlContextHolder.getWhere().forEach(item -> { + whereParam.append(((WhereModel) item).getSqlString()); + }); + where = StringUtils.isEmpty(where) ? whereParam.toString().substring(5, whereParam.length()) + : where + " " + whereParam.toString(); + plain.setWhere(CCJSqlParserUtil.parseCondExpression(where)); + } + + private static void handleJoin(Select select) { + PlainSelect selectBody = select.getPlainSelect(); + if (SqlContextHolder.getJoinTables() == null || SqlContextHolder.getJoinTables().size() <= 0) { + return; + } + SqlContextHolder.getJoinTables().forEach(item -> { + JoinTableModel tableModel = (JoinTableModel) item; + Table table = new Table(tableModel.getJoinTable()); + table.setAlias(new Alias(tableModel.getJoinTableAlise())); + Join join = new Join(); + join.setRightItem(table); + join.setInner(true); + Expression onExpression = new EqualsTo(new Column(tableModel.getFromTableColumnString()), + new Column(tableModel.getJoinTableColumnString())); + join.setOnExpressions(List.of(onExpression)); + selectBody.addJoins(join); + }); + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..76d0776c263eb546e4be3f5f5c8424ba1b93fc11 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PageAfterHandler.java @@ -0,0 +1,31 @@ +package com.ruoyi.mybatisinterceptor.sql.page; + +import java.util.List; + +import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; +import com.ruoyi.mybatisinterceptor.context.page.PageContextHolder; +import com.ruoyi.mybatisinterceptor.context.page.model.TableInfo; +import com.ruoyi.mybatisinterceptor.sql.MybatisAfterHandler; +import org.springframework.stereotype.Component; + + + +@MybatisHandlerOrder(1) +@Component +public class PageAfterHandler implements MybatisAfterHandler { + + @Override + public Object handleObject(Object object) throws Throwable { + if (PageContextHolder.isPage()) { + if (object instanceof List) { + TableInfo tableInfo = new TableInfo<>((List) object); + tableInfo.setTotal(PageContextHolder.getTotal()); + PageContextHolder.clear(); + return tableInfo; + } + return object; + } + return object; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..f7f771a44076bc535dee25e069adcdf26204a4b5 --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/sql/page/PagePreHandler.java @@ -0,0 +1,142 @@ +package com.ruoyi.mybatisinterceptor.sql.page; + +import java.lang.reflect.Field; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.ruoyi.mybatisinterceptor.annotation.MybatisHandlerOrder; +import com.ruoyi.mybatisinterceptor.context.page.PageContextHolder; +import com.ruoyi.mybatisinterceptor.context.page.model.PageInfo; +import com.ruoyi.mybatisinterceptor.sql.MybatisPreHandler; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.ResultMap; +import org.apache.ibatis.mapping.ResultMapping; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; +import org.springframework.stereotype.Component; +import org.springframework.util.ReflectionUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import net.sf.jsqlparser.schema.Column; +import net.sf.jsqlparser.statement.Statement; +import net.sf.jsqlparser.statement.select.Limit; +import net.sf.jsqlparser.statement.select.PlainSelect; +import net.sf.jsqlparser.statement.select.Select; +import net.sf.jsqlparser.statement.select.SelectItem; + +@Component +@MybatisHandlerOrder(2) +public class PagePreHandler implements MybatisPreHandler { + + private static final List EMPTY_RESULTMAPPING = new ArrayList(0); + + private static final String SELECT_COUNT_SUFIX = "_SELECT_COUNT"; + private static final Field sqlFiled = ReflectionUtils.findField(BoundSql.class, "sql"); + static { + sqlFiled.setAccessible(true); + } + + @Override + public void preHandle(Executor executor, MappedStatement mappedStatement, Object params, RowBounds rowBounds, + ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws Throwable { + if (PageContextHolder.isPage()) { + String originSql = boundSql.getSql(); + Statement sql = SqlUtil.parseSql(originSql); + if (sql instanceof Select) { + PageInfo pageInfo = PageContextHolder.getPageInfo(); + Statement handleLimit = handleLimit((Select) sql, pageInfo); + Statement countSql = getCountSql((Select) sql); + Long count = getCount(executor, mappedStatement, params, boundSql, rowBounds, resultHandler, + countSql.toString()); + PageContextHolder.setTotal(count); + sqlFiled.set(boundSql, handleLimit.toString()); + cacheKey = executor.createCacheKey(mappedStatement, params, rowBounds, boundSql); + } + } + + } + + private static MappedStatement createCountMappedStatement(MappedStatement ms, String newMsId) { + MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), newMsId, + ms.getSqlSource(), + ms.getSqlCommandType()); + builder.resource(ms.getResource()); + builder.fetchSize(ms.getFetchSize()); + builder.statementType(ms.getStatementType()); + builder.keyGenerator(ms.getKeyGenerator()); + if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) { + StringBuilder keyProperties = new StringBuilder(); + for (String keyProperty : ms.getKeyProperties()) { + keyProperties.append(keyProperty).append(","); + } + keyProperties.delete(keyProperties.length() - 1, keyProperties.length()); + builder.keyProperty(keyProperties.toString()); + } + builder.timeout(ms.getTimeout()); + builder.parameterMap(ms.getParameterMap()); + // count查询返回值int + List resultMaps = new ArrayList(); + ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Long.class, + EMPTY_RESULTMAPPING) + .build(); + resultMaps.add(resultMap); + builder.resultMaps(resultMaps); + builder.resultSetType(ms.getResultSetType()); + builder.cache(ms.getCache()); + builder.flushCacheRequired(ms.isFlushCacheRequired()); + builder.useCache(ms.isUseCache()); + return builder.build(); + } + + public static Long getCount(Executor executor, MappedStatement mappedStatement, Object parameter, + BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler, String countSql) + throws SQLException { + + Map additionalParameters = boundSql.getAdditionalParameters(); + + BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, + boundSql.getParameterMappings(), parameter); + for (String key : additionalParameters.keySet()) { + countBoundSql.setAdditionalParameter(key, additionalParameters.get(key)); + } + CacheKey countKey = executor.createCacheKey(mappedStatement, parameter, RowBounds.DEFAULT, countBoundSql); + + List query = executor.query( + createCountMappedStatement(mappedStatement, getCountMSId(mappedStatement)), + parameter, RowBounds.DEFAULT, resultHandler, countKey, + countBoundSql); + return (Long) query.get(0); + } + + private static String getCountMSId(MappedStatement mappedStatement) { + return mappedStatement.getId() + SELECT_COUNT_SUFIX; + } + + public static Statement getCountSql(Select select) { + PlainSelect plain = select.getPlainSelect(); + PlainSelect countPlain = new PlainSelect(); + countPlain.setSelectItems(List.of(new SelectItem<>(new Column("COUNT(0)")))); + countPlain.setJoins(plain.getJoins()); + countPlain.setWhere(plain.getWhere()); + countPlain.setFromItem(plain.getFromItem()); + countPlain.setDistinct(plain.getDistinct()); + countPlain.setHaving(plain.getHaving()); + countPlain.setIntoTables(plain.getIntoTables()); + // countPlain.setOrderByElements(plain.getOrderByElements()); + return plain; + } + + private static Statement handleLimit(Select select, PageInfo pageInfo) { + Limit limit = new Limit(); + limit.setRowCount(new Column(pageInfo.getPageSize().toString())); + limit.setOffset(new Column(pageInfo.getOffeset().toString())); + PlainSelect plain = select.getPlainSelect(); + plain.setLimit(limit); + return select; + } + +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..cb360d59d8632a88c5ddbede3dfa3d083a18e1da --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/DataSecurityUtil.java @@ -0,0 +1,15 @@ +package com.ruoyi.mybatisinterceptor.util; + + +import com.ruoyi.mybatisinterceptor.context.dataSecurity.SqlContextHolder; + +public class DataSecurityUtil { + + public static void closeDataSecurity() { + SqlContextHolder.clearCache(); + } + + public static void startDataSecurity() { + SqlContextHolder.startDataSecurity(); + } +} diff --git a/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/SqlUtil.java b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/SqlUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..98e1f5467714c18b1b398fd0495cd24cd063bfae --- /dev/null +++ b/ruoyi-plugins/ruoyi-mybatis-interceptor/src/main/java/com/ruoyi/mybatisinterceptor/util/SqlUtil.java @@ -0,0 +1,64 @@ +package com.ruoyi.mybatisinterceptor.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)); + } +} diff --git a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml index 0b4107f9d66656bb8631419ac1743815e1a7b095..183d4cd608fb03ac0dc269ebb08c7c88fed1cfcc 100644 --- a/ruoyi-plugins/ruoyi-plugins-starter/pom.xml +++ b/ruoyi-plugins/ruoyi-plugins-starter/pom.xml @@ -45,6 +45,11 @@ com.ruoyi ruoyi-mybatis-plus + + + com.ruoyi + ruoyi-mybatis-interceptor +