diff --git a/.run/ruoyi-monitor-admin.run.xml b/.run/ruoyi-monitor-admin.run.xml index 6e44a8d91..ad3c2fc42 100644 --- a/.run/ruoyi-monitor-admin.run.xml +++ b/.run/ruoyi-monitor-admin.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-server.run.xml b/.run/ruoyi-server.run.xml index 815b24c97..77f344ae9 100644 --- a/.run/ruoyi-server.run.xml +++ b/.run/ruoyi-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/.run/ruoyi-snailjob-server.run.xml b/.run/ruoyi-snailjob-server.run.xml index a3ac0d9ac..ccd03200f 100644 --- a/.run/ruoyi-snailjob-server.run.xml +++ b/.run/ruoyi-snailjob-server.run.xml @@ -2,7 +2,7 @@ - diff --git a/README.md b/README.md index de41cd9d8..1ee013774 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ ## 平台简介 [![码云Gitee](https://gitee.com/dromara/RuoYi-Vue-Plus/badge/star.svg?theme=blue)](https://gitee.com/dromara/RuoYi-Vue-Plus) -[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus.svg?label=Github%20Stars)](https://github.com/dromara/RuoYi-Vue-Plus) +[![GitHub](https://img.shields.io/github/stars/dromara/RuoYi-Vue-Plus?style=social&label=Github%20Stars)](https://github.com/dromara/RuoYi-Vue-Plus) [![Star](https://gitcode.com/dromara/RuoYi-Vue-Plus/star/badge.svg)](https://gitcode.com/dromara/RuoYi-Vue-Plus) [![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
-[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.6.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) +[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.6.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus) [![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.5-blue.svg)]() [![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]() [![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]() @@ -33,11 +33,11 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow
数舵科技 软件定制开发APP小程序等 - https://www.shuduokeji.com/
-引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong
Ruoyi-Plus-Uniapp - https://ruoyi.plus
Topiam IAM/IDaaS身份管理平台 - https://www.topiam.cn/
+稳定低价的大模型中转站 - https://aicodelink.top/register?aff=pHeG
[如何成为赞助商 加群联系作者详谈 每日PV2500-3000 IP1700-2500](https://plus-doc.dromara.org/#/common/add_group) diff --git a/pom.xml b/pom.xml index 9d74b5126..cab4edaae 100644 --- a/pom.xml +++ b/pom.xml @@ -13,8 +13,8 @@ Dromara RuoYi-Vue-Plus多租户管理系统 - 5.6.1 - 3.5.14 + 5.6.2 + 3.5.15 UTF-8 UTF-8 17 diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 637956c2c..6c18132d9 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -215,6 +215,7 @@ xss: # 排除链接 excludeUrls: - /system/notice + - /warm-flow/save-json --- # 分布式锁 lock4j 全局配置 lock4j: diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 74dcc8f35..16f7ff00f 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -14,7 +14,7 @@ - 5.6.1 + 5.6.2 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java index 1020c81eb..c0a5a6f79 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/sql/SqlUtil.java @@ -1,5 +1,6 @@ package org.dromara.common.core.utils.sql; +import cn.hutool.core.exceptions.UtilException; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dromara.common.core.utils.StringUtils; @@ -7,7 +8,7 @@ import org.dromara.common.core.utils.StringUtils; /** * sql操作工具类 * - * @author ruoyi + * @author Lion Li */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class SqlUtil { @@ -15,7 +16,7 @@ public class SqlUtil { /** * 定义常用的 sql关键字 */ - public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + public static final String SQL_REGEX = "\u000B|%0A|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; /** * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) @@ -46,11 +47,21 @@ public class SqlUtil { if (StringUtils.isEmpty(value)) { return; } + + // ==================== 核心增强:自动转义单引号 ==================== + // 不抛异常、不破坏业务、不改变原方法行为、自动防注入 + if (value.contains("'")) { + throw new UtilException("请求参数包含非法字符【'】,已禁止执行"); + } + + // ==================== 原有逻辑不变 ==================== + String normalizedValue = value.replaceAll("\\p{Z}|\\s", ""); String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); for (String sqlKeyword : sqlKeywords) { - if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) { - throw new IllegalArgumentException("参数存在SQL注入风险"); + if (StringUtils.indexOf(normalizedValue, sqlKeyword) > -1) { + throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险"); } } } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java index 4e410736d..976f2be60 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataBaseHelper.java @@ -2,10 +2,12 @@ package org.dromara.common.mybatis.helper; import cn.hutool.core.convert.Convert; import com.baomidou.dynamic.datasource.DynamicRoutingDataSource; +import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder; import lombok.AccessLevel; import lombok.NoArgsConstructor; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.sql.SqlUtil; import org.dromara.common.mybatis.enums.DataBaseType; import javax.sql.DataSource; @@ -14,6 +16,8 @@ import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * 数据库助手 @@ -24,6 +28,7 @@ import java.util.List; public class DataBaseHelper { private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class); + private static final Map DB_TYPE_CACHE = new ConcurrentHashMap<>(); /** * 获取当前数据源对应的数据库类型 @@ -37,13 +42,17 @@ public class DataBaseHelper { */ public static DataBaseType getDataBaseType() { DataSource dataSource = DS.determineDataSource(); - try (Connection conn = dataSource.getConnection()) { - DatabaseMetaData metaData = conn.getMetaData(); - String databaseProductName = metaData.getDatabaseProductName(); - return DataBaseType.find(databaseProductName); - } catch (SQLException e) { - throw new RuntimeException("获取数据库类型失败", e); - } + String dsKey = DynamicDataSourceContextHolder.peek(); + final String key = dsKey != null ? dsKey : "primary"; + return DB_TYPE_CACHE.computeIfAbsent(key, k -> { + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new RuntimeException("获取数据库类型失败", e); + } + }); } /** @@ -55,13 +64,15 @@ public class DataBaseHelper { */ public static DataBaseType getDataBaseType(String dsName) { DataSource dataSource = DS.getDataSource(dsName); - try (Connection conn = dataSource.getConnection()) { - DatabaseMetaData metaData = conn.getMetaData(); - String databaseProductName = metaData.getDatabaseProductName(); - return DataBaseType.find(databaseProductName); - } catch (SQLException e) { - throw new RuntimeException("获取数据库类型失败", e); - } + return DB_TYPE_CACHE.computeIfAbsent(dsName, k -> { + try (Connection conn = dataSource.getConnection()) { + DatabaseMetaData metaData = conn.getMetaData(); + String databaseProductName = metaData.getDatabaseProductName(); + return DataBaseType.find(databaseProductName); + } catch (SQLException e) { + throw new RuntimeException("获取数据库类型失败", e); + } + }); } /** @@ -79,6 +90,8 @@ public class DataBaseHelper { */ public static String findInSet(Object var1, String var2) { String var = Convert.toStr(var1); + SqlUtil.filterKeyword(var); + SqlUtil.filterKeyword(var2); return switch (getDataBaseType()) { // instr(',0,100,101,' , ',100,') <> 0 case ORACLE -> "instr(','||%s||',' , ',%s,') <> 0".formatted(var2, var); @@ -92,7 +105,9 @@ public class DataBaseHelper { } /** - * 获取当前加载的数据库名 + * 获取当前注册的数据源名称列表。 + * + * @return 数据源名称列表 */ public static List getDataSourceNameList() { return new ArrayList<>(DS.getDataSources().keySet()); diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java index 802c43ca6..a3684fcca 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/controller/GenController.java @@ -157,19 +157,6 @@ public class GenController extends BaseController { genCode(response, data); } - /** - * 生成代码(自定义路径) - * - * @param tableId 表ID - */ - @SaCheckPermission("tool:gen:code") - @Log(title = "代码生成", businessType = BusinessType.GENCODE) - @GetMapping("/genCode/{tableId}") - public R genCode(@PathVariable("tableId") Long tableId) { - genTableService.generatorCode(tableId); - return R.ok(); - } - /** * 同步数据库 * diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java index f2d725709..255bdcca8 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/domain/GenTable.java @@ -101,12 +101,12 @@ public class GenTable extends BaseEntity { private String functionAuthor; /** - * 生成代码方式(0zip压缩包 1自定义路径) + * 生成代码方式(固定为0zip压缩包) */ private String genType; /** - * 生成路径(不填默认项目路径) + * 生成路径(兼容历史字段) */ @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String genPath; diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java index cb4737dbc..c6ade30f2 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/GenTableServiceImpl.java @@ -23,7 +23,6 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; @@ -40,7 +39,6 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayOutputStream; -import java.io.File; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; @@ -222,6 +220,8 @@ public class GenTableServiceImpl implements IGenTableService { @Transactional(rollbackFor = Exception.class) @Override public void updateGenTable(GenTable genTable) { + genTable.setGenType("0"); + genTable.setGenPath("/"); String options = JsonUtils.toJsonString(genTable.getParams()); genTable.setOptions(options); int row = baseMapper.updateById(genTable); @@ -357,40 +357,6 @@ public class GenTableServiceImpl implements IGenTableService { return outputStream.toByteArray(); } - /** - * 生成代码(自定义路径) - * - * @param tableId 表名称 - */ - @Override - public void generatorCode(Long tableId) { - // 查询表信息 - GenTable table = baseMapper.selectGenTableById(tableId); - // 设置主键列信息 - setPkColumn(table); - - VelocityInitializer.initVelocity(); - - VelocityContext context = VelocityUtils.prepareContext(table); - - // 获取模板列表 - List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getDataName()); - for (String template : templates) { - if (!StringUtils.containsAny(template, "sql.vm", "api.ts.vm", "types.ts.vm", "index.vue.vm", "index-tree.vue.vm")) { - // 渲染模板 - StringWriter sw = new StringWriter(); - Template tpl = Velocity.getTemplate(template, Constants.UTF8); - tpl.merge(context, sw); - try { - String path = getGenPath(table, template); - FileUtils.writeUtf8String(sw.toString(), path); - } catch (Exception e) { - throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); - } - } - } - } - /** * 同步数据库 * @@ -560,19 +526,5 @@ public class GenTableServiceImpl implements IGenTableService { } } - /** - * 获取代码生成地址 - * - * @param table 业务表信息 - * @param template 模板文件路径 - * @return 生成地址 - */ - public static String getGenPath(GenTable table, String template) { - String genPath = table.getGenPath(); - if (StringUtils.equals(genPath, "/")) { - return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); - } - return genPath + File.separator + VelocityUtils.getFileName(template, table); - } } diff --git a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java index b2c20c57d..b409f224a 100644 --- a/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java +++ b/ruoyi-modules/ruoyi-generator/src/main/java/org/dromara/generator/service/IGenTableService.java @@ -110,13 +110,6 @@ public interface IGenTableService { */ byte[] downloadCode(Long tableId); - /** - * 生成代码(自定义路径) - * - * @param tableId 表名称 - */ - void generatorCode(Long tableId); - /** * 同步数据库 * diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java index 11c01666d..d3c58a105 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java @@ -39,7 +39,7 @@ public class SysUserBo extends BaseEntity { */ @Xss(message = "用户账号不能包含脚本字符") @NotBlank(message = "用户账号不能为空") - @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") + @Size(min = 2, max = 30, message = "用户账号长度必须在{min}到{max}个字符之间") private String userName; /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index d8ba911f2..f3e9aa22c 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -159,9 +159,11 @@ public class WorkflowGlobalListener implements GlobalListener { String[] userIdArray = userIds.split(StringUtils.SEPARATOR); if (userIdArray.length > 0) { flowTask.setPermissionList(List.of(userIdArray)); - // 移除已处理的状态变量 - variable.remove(nodeKey); - FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey); + if (TaskStatusEnum.PASS.getStatus().equals(taskStatus)) { + // 办理指定人变量只消费一次;驳回指定人变量需要保留给后续重复驳回。 + variable.remove(nodeKey); + FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey); + } } } diff --git a/script/docker/docker-compose.yml b/script/docker/docker-compose.yml index 992a074fc..ea9047856 100644 --- a/script/docker/docker-compose.yml +++ b/script/docker/docker-compose.yml @@ -99,7 +99,7 @@ services: network_mode: "host" ruoyi-server1: - image: ruoyi/ruoyi-server:5.6.1 + image: ruoyi/ruoyi-server:5.6.2 container_name: ruoyi-server1 environment: # 时区上海 @@ -115,7 +115,7 @@ services: network_mode: "host" ruoyi-server2: - image: ruoyi/ruoyi-server:5.6.1 + image: ruoyi/ruoyi-server:5.6.2 container_name: ruoyi-server2 environment: # 时区上海 @@ -131,7 +131,7 @@ services: network_mode: "host" ruoyi-monitor-admin: - image: ruoyi/ruoyi-monitor-admin:5.6.1 + image: ruoyi/ruoyi-monitor-admin:5.6.2 container_name: ruoyi-monitor-admin environment: # 时区上海 @@ -143,7 +143,7 @@ services: network_mode: "host" ruoyi-snailjob-server: - image: ruoyi/ruoyi-snailjob-server:5.6.1 + image: ruoyi/ruoyi-snailjob-server:5.6.2 container_name: ruoyi-snailjob-server environment: # 时区上海