Spring Boot项目中基于log4j2通过注解实现DTO敏感字段日志脱敏咨询
Spring Boot + Log4j2 日志场景专属敏感字段脱敏实现方案
核心思路是替换Log4j2默认的消息工厂,在日志参数被转为字符串前拦截原始对象,仅针对日志打印场景执行脱敏处理,不影响业务层的序列化、对象操作逻辑。
1. 定义敏感字段标记注解
import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Sensitive { // 需要脱敏的字段名 String[] fields() default {}; }
使用方式和你给出的示例一致:
@Sensitive(fields = {"password", "email"}) public class MyDTO { private String name; private String email; private String password; // 省略getter/setter }
2. 实现脱敏工具类
处理时会创建对象副本,不会修改原对象的属性值,避免影响业务逻辑:
import org.springframework.beans.BeanUtils; import java.lang.reflect.Field; import java.util.Arrays; import java.util.List; public class SensitiveUtils { public static Object handleSensitive(Object obj) { if (obj == null) return null; Class<?> clazz = obj.getClass(); // 没有标注@Sensitive注解直接返回原对象 if (!clazz.isAnnotationPresent(Sensitive.class)) { return obj; } try { // 创建对象副本,避免修改原对象 Object copyObj = clazz.getDeclaredConstructor().newInstance(); BeanUtils.copyProperties(obj, copyObj); Sensitive sensitiveAnno = clazz.getAnnotation(Sensitive.class); List<String> sensitiveFields = Arrays.asList(sensitiveAnno.fields()); // 替换敏感字段值 for (String fieldName : sensitiveFields) { Field field = clazz.getDeclaredField(fieldName); field.setAccessible(true); Object value = field.get(copyObj); if (value instanceof String strVal) { // 可根据字段类型自定义脱敏规则,比如邮箱保留前缀1位和@后缀,密码全替换为*** if (fieldName.equals("email")) { int atIndex = strVal.indexOf("@"); if (atIndex > 1) { field.set(copyObj, strVal.charAt(0) + "***" + strVal.substring(atIndex)); } else { field.set(copyObj, "***"); } } else if (fieldName.equals("password")) { field.set(copyObj, "***"); } else { // 默认脱敏规则:保留前1后1,中间替换为*** if (strVal.length() <= 2) { field.set(copyObj, "***"); } else { field.set(copyObj, strVal.charAt(0) + "***" + strVal.charAt(strVal.length()-1)); } } } } return copyObj; } catch (Exception e) { // 脱敏异常直接返回原对象,避免影响日志打印 return obj; } } }
3. 自定义Log4j2消息工厂
替换默认的参数化消息工厂,在生成日志消息前先处理所有参数的敏感字段:
import org.apache.logging.log4j.message.AbstractMessageFactory; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; public class SensitiveMessageFactory extends AbstractMessageFactory { @Override public Message newMessage(String message, Object... params) { if (params == null || params.length == 0) { return new ParameterizedMessage(message, params); } // 遍历所有日志参数,处理敏感字段 Object[] handledParams = new Object[params.length]; for (int i = 0; i < params.length; i++) { handledParams[i] = SensitiveUtils.handleSensitive(params[i]); } return new ParameterizedMessage(message, handledParams); } }
4. 配置Log4j2使用自定义消息工厂
在你的log4j2.xml配置文件的根标签<Configuration>上添加messageFactory属性指定自定义工厂类即可:
<Configuration status="warn" messageFactory="com.yourpackage.SensitiveMessageFactory"> <!-- 其余原有配置保持不变 --> <Appenders> ... </Appenders> <Loggers> ... </Loggers> </Configuration>
方案优势
- 仅在日志打印场景触发脱敏,不会影响业务层的JSON序列化、对象原生
toString()调用 - 日志参数还未转为字符串时就完成脱敏,能直接拿到原始对象的注解配置
- 无侵入性,原有日志打印代码不需要做任何修改
内容的提问来源于stack exchange,提问作者Eugene
相关产品推荐
相关产品推荐

