update 优化日志参数字段排除处理 采用更好的JsonNode节点树处理 性能更好 避免报错

This commit is contained in:
疯狂的狮子Li
2026-06-15 09:39:25 +08:00
parent 44e17a5bb0
commit 64861cc822
3 changed files with 44 additions and 70 deletions
@@ -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(对象或数组)
*
@@ -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<Dict> 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);
}
/**
@@ -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<String> 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());