From 463a18f209f51b24a82b77669c8b15b14be2b54f Mon Sep 17 00:00:00 2001 From: 16358 <1635849544@qq.com> Date: Tue, 3 Jun 2025 11:20:06 +0800 Subject: [PATCH] =?UTF-8?q?components=E6=B7=BB=E5=8A=A0=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=BB=84=E4=BB=B6searchSelect?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/ruoyi/common/utils/sql/SqlUtil.java | 108 +++++++++++ .../generator/controller/GenController.java | 19 ++ .../com/ruoyi/generator/domain/GenTable.java | 12 ++ .../generator/domain/GenTableColumn.java | 10 + .../generator/mapper/GenTableMapper.java | 4 + .../service/GenTableServiceImpl.java | 6 + .../generator/service/IGenTableService.java | 2 + .../mapper/generator/GenTableMapper.xml | 17 ++ ruoyi-ui/src/api/tool/gen.js | 8 + .../src/components/SearchSelect/index.vue | 177 ++++++++++++++++++ 10 files changed, 363 insertions(+) create mode 100644 ruoyi-ui/src/components/SearchSelect/index.vue 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 48720dc..792a308 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 @@ -3,6 +3,9 @@ package com.ruoyi.common.utils.sql; import com.ruoyi.common.exception.UtilException; import com.ruoyi.common.utils.StringUtils; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + /** * sql操作工具类 * @@ -25,6 +28,25 @@ public class SqlUtil */ private static final int ORDER_BY_MAX_LENGTH = 500; + private final static String XSS_STR = "and |extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|or |+|user()"; + + /** + * 正则 user() 匹配更严谨 + */ + private final static String REGULAR_EXPRE_USER = "user[\\s]*\\([\\s]*\\)"; + /**正则 show tables*/ + private final static String SHOW_TABLES = "show\\s+tables"; + + /** + * sleep函数 + */ + private final static Pattern FUN_SLEEP = Pattern.compile("sleep\\(.*\\)", Pattern.CASE_INSENSITIVE); + + /** + * sql注释的正则 + */ + private final static Pattern SQL_ANNOTATION = Pattern.compile("/\\*[\\s\\S]*\\*/"); + /** * 检查字符,防止注入绕过 */ @@ -67,4 +89,90 @@ public class SqlUtil } } } + + /** + * 返回查询表名 + *

+ * sql注入过滤处理,遇到注入关键字抛异常 + * + * @param table + */ + private static Pattern tableNamePattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]{0,63}$"); + public static String getSqlInjectTableName(String table) { + table = table.trim(); + /** + * 检验表名是否合法 + * + * 表名只能由字母、数字和下划线组成。 + * 表名必须以字母开头。 + * 表名长度通常有限制,例如最多为 64 个字符。 + */ + boolean isValidTableName = tableNamePattern.matcher(table).matches(); + if (!isValidTableName) { + String errorMsg = "表名不合法,存在SQL注入风险!--->" + table; + throw new UtilException(errorMsg); + } + + //进一步验证是否存在SQL注入风险 + filterContent(table, null); + return table; + } + + /** + * sql注入过滤处理,遇到注入关键字抛异常 + * + * @param value + * @return + */ + public static void filterContent(String value, String customXssString) { + if (value == null || "".equals(value)) { + return; + } + // 校验sql注释 不允许有sql注释 + checkSqlAnnotation(value); + // 统一转为小写 + value = value.toLowerCase(); + //SQL注入检测存在绕过风险 https://gitee.com/jeecg/jeecg-boot/issues/I4NZGE + //value = value.replaceAll("/\\*.*\\*/",""); + + String[] xssArr = XSS_STR.split("\\|"); + for (int i = 0; i < xssArr.length; i++) { + if (value.indexOf(xssArr[i]) > -1) { + throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + //update-begin-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if (customXssString != null) { + String[] xssArr2 = customXssString.split("\\|"); + for (int i = 0; i < xssArr2.length; i++) { + if (value.indexOf(xssArr2[i]) > -1) { + throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value); + } + } + } + //update-end-author:taoyan date:2022-7-13 for: 除了XSS_STR这些提前设置好的,还需要额外的校验比如 单引号 + if(Pattern.matches(SHOW_TABLES, value) || Pattern.matches(REGULAR_EXPRE_USER, value)){ + throw new UtilException("请注意,值可能存在SQL注入风险!--->" + value); + } + return; + } + + /** + * 校验是否有sql注释 + * @return + */ + public static void checkSqlAnnotation(String str){ + Matcher matcher = SQL_ANNOTATION.matcher(str); + if(matcher.find()){ + String error = "请注意,值可能存在SQL注入风险---> \\*.*\\"; + throw new UtilException(error); + } + + // issues/4737 sys/duplicate/check SQL注入 #4737 + Matcher sleepMatcher = FUN_SLEEP.matcher(str); + if(sleepMatcher.find()){ + String error = "请注意,值可能存在SQL注入风险---> sleep"; + throw new UtilException(error); + } + } } diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java index 1ef0d35..b044c5c 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -260,4 +260,23 @@ public class GenController extends BaseController response.setContentType("application/octet-stream; charset=UTF-8"); IOUtils.write(data, response.getOutputStream()); } + + /** + * 查询业务列表 + */ + @GetMapping("/getSearchSelectData") + public AjaxResult getSearchSelectData( String tableName, String value, String label, String where, String groupby, String orderby) + { + orderby = SqlUtil.escapeOrderBySql(orderby); + tableName = SqlUtil.getSqlInjectTableName(tableName); + Map map = new HashMap<>(); + map.put("tableName", tableName); + map.put("value", SqlUtil.getSqlInjectTableName(value)); + map.put("label", SqlUtil.getSqlInjectTableName(label)); + map.put("where", where); + map.put("groupby", groupby); + map.put("orderby", orderby); + List list = genTableService.getSearchSelectData(map); + return AjaxResult.success(list); + } } \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java index 022a54d..797a001 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -98,6 +98,10 @@ public class GenTable extends BaseEntity /** 上级菜单名称字段 */ private String parentMenuName; + private String remark; + + private Integer columnCount; + public Long getTableId() { return tableId; @@ -343,6 +347,14 @@ public class GenTable extends BaseEntity return isSub(this.tplCategory); } + public Integer getColumnCount() { + return columnCount; + } + + public void setColumnCount(Integer columnCount) { + this.columnCount = columnCount; + } + public static boolean isSub(String tplCategory) { return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java index d1733b6..076b7a5 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -19,6 +19,8 @@ public class GenTableColumn extends BaseEntity /** 归属表编号 */ private Long tableId; + private String tableName; + /** 列名称 */ private String columnName; @@ -78,6 +80,14 @@ public class GenTableColumn extends BaseEntity return columnId; } + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + public void setTableId(Long tableId) { this.tableId = tableId; diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java index 937656d..9c45784 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -1,6 +1,8 @@ package com.ruoyi.generator.mapper; import java.util.List; +import java.util.Map; + import com.ruoyi.generator.domain.GenTable; /** @@ -88,4 +90,6 @@ public interface GenTableMapper * @return 结果 */ public int createTable(String sql); + + List getSearchSelectData(Map map); } diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java index fe4312f..cc64855 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -528,4 +528,10 @@ public class GenTableServiceImpl implements IGenTableService } return genPath + File.separator + VelocityUtils.getFileName(template, table); } + + @Override + public List getSearchSelectData(Map map ) { + + return genTableMapper.getSearchSelectData(map); + } } \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java index 695426e..6cecbf8 100644 --- a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -127,4 +127,6 @@ public interface IGenTableService * @param genTable 业务信息 */ public void validateEdit(GenTable genTable); + + List getSearchSelectData(Map map ); } diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml index d1110f7..5e3921f 100644 --- a/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableMapper.xml @@ -174,6 +174,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" ${sql} + + update gen_table diff --git a/ruoyi-ui/src/api/tool/gen.js b/ruoyi-ui/src/api/tool/gen.js index 2075677..d5f01f7 100644 --- a/ruoyi-ui/src/api/tool/gen.js +++ b/ruoyi-ui/src/api/tool/gen.js @@ -83,3 +83,11 @@ export function synchDb(tableName) { method: 'get' }) } + +export function getSearchSelectData(param){ + return request({ + url: '/tool/gen/getSearchSelectData', + method: 'get', + params: param + }) +} diff --git a/ruoyi-ui/src/components/SearchSelect/index.vue b/ruoyi-ui/src/components/SearchSelect/index.vue new file mode 100644 index 0000000..e884e4a --- /dev/null +++ b/ruoyi-ui/src/components/SearchSelect/index.vue @@ -0,0 +1,177 @@ + +