fix: 移除FastJson 1.2.83严重安全漏洞,替换为Jackson

- 移除FastJson 1.2.83依赖(存在严重RCE漏洞CVE-2022-25845等)
- 替换为Spring Boot内置的Jackson 2.18.2
- 修改6个Java文件的JSON处理逻辑
- 所有模块编译验证通过

修改文件:
1. pom.xml - 移除fastjson依赖定义
2. ruoyi-common-chat/pom.xml - 替换为jackson-databind
3. QwenFileUploadUtils.java - 千问文件上传JSON解析
4. ChatRequest.java - 移除FastJson注解
5. MailSendNode.java - 邮件节点JSON处理
6. SwitcherNode.java - 条件分支JSON处理
7. AbstractAuthWeChatEnterpriseRequest.java - 企业微信登录
8. AuthDingTalkV2Request.java - 钉钉登录

安全提升:消除FastJson反序列化RCE漏洞攻击面

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ageerle
2026-07-29 14:59:20 +08:00
parent 7640de34e5
commit 83fd1ee983
9 changed files with 287 additions and 117 deletions

View File

@@ -1,8 +1,7 @@
package org.ruoyi.workflow.workflow.node.mailSend;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.JSONValidator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.mail.internet.MimeMessage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -41,10 +40,15 @@ public class MailSendNode extends AbstractWfNode {
String input = getDataFromInput(inputs);
// 判断是否为JSON格式(LLM输出转换 由LLM生成格式)
if (StringUtils.isNotBlank(input) && isJson(input)) {
JSONObject inputJson = JSON.parseObject(input);
JSONObject configJson = (JSONObject) JSON.toJSON(config);
configJson.putAll(inputJson);
config = configJson.toJavaObject(MailSendNodeConfig.class);
// 使用Jackson解析和合并配置
ObjectMapper objectMapper = new ObjectMapper();
JsonNode inputJson = objectMapper.readTree(input);
// 将config转换为JsonNode
JsonNode configJson = objectMapper.valueToTree(config);
// 合并两个JSON节点
JsonNode mergedJson = objectMapper.readerForUpdating(configJson).readValue(inputJson);
// 转换回config对象
config = objectMapper.treeToValue(mergedJson, MailSendNodeConfig.class);
}
// 安全获取模板(使用 defaultString 避免 null
@@ -217,9 +221,10 @@ public class MailSendNode extends AbstractWfNode {
if (str == null || str.trim().isEmpty()) {
return false;
}
// 使用 try-with-resources 正确处理 JSONValidator 资源关闭
try (JSONValidator validator = JSONValidator.from(str.trim())) {
return validator.getType() == JSONValidator.Type.Object;
try {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.readTree(str.trim());
return true;
} catch (Exception e) {
log.warn("JSON格式校验失败: {}", e.getMessage());
return false;

View File

@@ -1,8 +1,7 @@
package org.ruoyi.workflow.workflow.node.switcher;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
@@ -339,23 +338,26 @@ public class SwitcherNode extends AbstractWfNode {
String inputConfig = workflowNode.getInputConfig();
log.info("节点 '{}' 的输入配置: {}", nodeUuid, inputConfig);
if (StringUtils.isNotBlank(inputConfig)){
// 解析输入配置为JSON对象
JSONObject configJson = JSON.parseObject(inputConfig);
// 获取 user_inputs 数组
JSONArray userInputs = configJson.getJSONArray("user_inputs");
if (userInputs != null && !userInputs.isEmpty()) {
// 在 user_inputs 中查找匹配的参数名,并获取对应值
Optional<String> valueOpt = userInputs.stream()
.filter(JSONObject.class::isInstance)
.map(JSONObject.class::cast)
.filter(obj -> paramName.equals(obj.getString("name")))
.map(matchedObj -> getValueFromInputs(nodeUuid, "input", inputs))
.filter(Objects::nonNull)
.findFirst();
// 若找到匹配值,则更新结果
if (valueOpt.isPresent()) {
result = valueOpt.get();
try {
// 使用Jackson解析输入配置
ObjectMapper objectMapper = new ObjectMapper();
JsonNode configJson = objectMapper.readTree(inputConfig);
// 获取 user_inputs 数组
JsonNode userInputs = configJson.get("user_inputs");
if (userInputs != null && userInputs.isArray()) {
// 在 user_inputs 中查找匹配的参数名,并获取对应值
for (JsonNode inputNode : userInputs) {
if (inputNode.has("name") && paramName.equals(inputNode.get("name").asText())) {
String value = getValueFromInputs(nodeUuid, "input", inputs);
if (value != null) {
result = value;
break;
}
}
}
}
} catch (Exception e) {
log.error("解析节点输入配置失败: {}", nodeUuid, e);
}
}
}

View File

@@ -1,6 +1,7 @@
package org.ruoyi.system.utils;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.ruoyi.common.core.utils.StringUtils;
@@ -42,9 +43,11 @@ public class QwenFileUploadUtils {
if (StringUtils.isEmpty(responseBody)){
throw new ServerException("上传失败:响应体为空");
}
JSONObject jsonObject = JSONObject.parseObject(responseBody);
// 使用Jackson解析JSON
ObjectMapper objectMapper = new ObjectMapper();
JsonNode jsonNode = objectMapper.readTree(responseBody);
// 千问返回的 fileId
return jsonObject.getString("id");
return jsonNode.get("id").asText();
}
}
}