diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java index 20ada8f04..5b9f83219 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/utils/JsonUtils.java @@ -10,6 +10,7 @@ import org.dromara.common.core.utils.StringUtils; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.JsonNode; import tools.jackson.databind.json.JsonMapper; +import tools.jackson.databind.node.ObjectNode; import java.util.ArrayList; import java.util.List; @@ -142,6 +143,47 @@ public class JsonUtils { return JSON_MAPPER.readValue(text, JSON_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz)); } + /** + * 将对象转换为 JSON 字符串,并递归移除指定字段。 + * + * @param object 要转换的对象 + * @param fieldNames 需要移除的字段名 + * @return 移除字段后的 JSON 字符串 + */ + public static String toJsonStringExcludeFields(Object object, String... fieldNames) { + if (ObjectUtil.isNull(object)) { + return null; + } + JsonNode node = JSON_MAPPER.valueToTree(object); + removeFields(node, fieldNames); + return toJsonString(node); + } + + /** + * 从 JSON 树中递归移除指定字段。 + * + * @param node JSON 节点 + * @param fieldNames 需要移除的字段名 + * @return 原 JSON 节点 + */ + public static JsonNode removeFields(JsonNode node, String... fieldNames) { + if (node == null || ArrayUtil.isEmpty(fieldNames)) { + return node; + } + if (node.isObject()) { + ObjectNode objectNode = (ObjectNode) node; + for (String fieldName : fieldNames) { + if (StringUtils.isNotBlank(fieldName)) { + objectNode.remove(fieldName); + } + } + } + for (JsonNode child : node) { + removeFields(child, fieldNames); + } + return node; + } + /** * 判断字符串是否为合法 JSON(对象或数组) * diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java index 515430a49..091c80452 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java @@ -1,6 +1,5 @@ package org.dromara.common.log.aspect; -import cn.hutool.core.lang.Dict; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; @@ -212,39 +211,7 @@ public class LogAspect { * @return 参数日志字符串 */ private String serializeArg(Object arg, String[] exclude) { - if (arg instanceof Collection collection) { - List list = new ArrayList<>(collection.size()); - for (Object item : collection) { - Dict dict = toFilteredDict(item, exclude); - if (MapUtil.isNotEmpty(dict)) { - list.add(dict); - } - } - return JsonUtils.toJsonString(list); - } - String str = JsonUtils.toJsonString(arg); - Dict dict = JsonUtils.parseMap(str); - if (MapUtil.isNotEmpty(dict)) { - MapUtil.removeAny(dict, exclude); - return JsonUtils.toJsonString(dict); - } - return str; - } - - /** - * 将参数转为已排除指定字段的字典。 - * - * @param value 参数值 - * @param exclude 排除字段名 - * @return 已过滤字段的字典 - */ - private Dict toFilteredDict(Object value, String[] exclude) { - String str = JsonUtils.toJsonString(value); - Dict dict = JsonUtils.parseMap(str); - if (MapUtil.isNotEmpty(dict)) { - MapUtil.removeAny(dict, exclude); - } - return dict; + return JsonUtils.toJsonStringExcludeFields(arg, exclude); } /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java index 40016357e..d3025d487 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java @@ -2,7 +2,6 @@ package org.dromara.common.web.interceptor; import cn.hutool.core.io.IoUtil; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @@ -16,13 +15,9 @@ import org.springframework.http.MediaType; import org.springframework.web.servlet.HandlerInterceptor; import tools.jackson.databind.JsonNode; import tools.jackson.databind.json.JsonMapper; -import tools.jackson.databind.node.ArrayNode; -import tools.jackson.databind.node.ObjectNode; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; -import java.util.Set; /** * Web 调用时间统计拦截器,同时记录请求参数并对敏感字段做脱敏处理。 @@ -81,36 +76,6 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { return true; } - /** - * 递归移除 JSON 节点中的敏感字段,避免在日志中输出密码等敏感信息。 - * - * @param node 当前 JSON 节点 - * @param excludeProperties 需要排除的字段名集合 - */ - private void removeSensitiveFields(JsonNode node, String[] excludeProperties) { - if (node == null) { - return; - } - if (node.isObject()) { - ObjectNode objectNode = (ObjectNode) node; - // 收集要删除的字段名(避免 ConcurrentModification) - Set fieldsToRemove = new HashSet<>(); - objectNode.propertyNames().forEach(fieldName -> { - if (ArrayUtil.contains(excludeProperties, fieldName)) { - fieldsToRemove.add(fieldName); - } - }); - fieldsToRemove.forEach(objectNode::remove); - // 递归处理子节点 - objectNode.values().forEach(child -> removeSensitiveFields(child, excludeProperties)); - } else if (node.isArray()) { - ArrayNode arrayNode = (ArrayNode) node; - for (JsonNode child : arrayNode) { - removeSensitiveFields(child, excludeProperties); - } - } - } - /** * 清洗 JSON 请求参数日志,解析失败时不影响主请求。 * @@ -121,7 +86,7 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { try { JsonMapper jsonMapper = JsonUtils.getJsonMapper(); JsonNode rootNode = jsonMapper.readTree(jsonParam); - removeSensitiveFields(rootNode, SystemConstants.EXCLUDE_PROPERTIES); + JsonUtils.removeFields(rootNode, SystemConstants.EXCLUDE_PROPERTIES); return rootNode.toString(); } catch (Exception e) { log.debug("[PLUS]请求参数 JSON 解析失败,跳过结构化脱敏: {}", e.getMessage());