Files
baiyue-firstleg/common-audit/src/main/java/com/baiyue/common/audit/SensitiveDataSanitizer.java
T
2026-07-30 18:01:36 +08:00

53 lines
2.0 KiB
Java

package com.baiyue.common.audit;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
/**
* 审计数据脱敏器,在记录请求快照前递归屏蔽密码、令牌、密钥等敏感字段。
*/
public class SensitiveDataSanitizer {
private static final Set<String> SENSITIVE = Set.of("password", "passwordhash", "token", "accesstoken",
"refreshtoken", "authorization", "secret", "idcard", "bankcard", "mobile", "phone", "email");
private final ObjectMapper mapper;
private final int maxLength;
public SensitiveDataSanitizer(ObjectMapper mapper, int maxLength) {
this.mapper = mapper;
this.maxLength = maxLength;
}
public String sanitize(Object value) {
if (value == null) return null;
try {
JsonNode root = mapper.valueToTree(value);
redact(root);
String json = mapper.writeValueAsString(root);
return json.length() <= maxLength ? json : json.substring(0, maxLength) + "...[truncated]";
} catch (IllegalArgumentException | JsonProcessingException e) {
return "\"[unserializable]\"";
}
}
private void redact(JsonNode node) {
if (node == null) return;
if (node.isObject()) {
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
String normalized = field.getKey().replace("_", "").toLowerCase(Locale.ROOT);
if (SENSITIVE.stream().anyMatch(normalized::contains)) {
((ObjectNode) node).put(field.getKey(), "******");
} else redact(field.getValue());
}
} else if (node.isArray()) node.forEach(this::redact);
}
}