4.log4j2实现日志脱敏
现在项目中没有对日志脱敏,于是我萌生了研究日志脱敏的想法。之前对日志系统没有深入了解过,总结了一下日志的知识点:
- 日志基础框架图

首先看一下最终脱敏效果:
具体步骤如下:
方案是参考官网的RewriteAppender,实现日志脱敏。
1.重写RewritePolicy中的rewrite方法,在每次打印日志之前进行一次数据过滤,将敏感字段进行加密。
package com.zxy.demo.config; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.google.common.base.Objects; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.core.LogEvent; import org.apache.logging.log4j.core.appender.rewrite.RewritePolicy; import org.apache.logging.log4j.core.config.plugins.Plugin; import org.apache.logging.log4j.core.config.plugins.PluginFactory; import org.apache.logging.log4j.core.impl.Log4jLogEvent; import org.apache.logging.log4j.message.Message; import org.apache.logging.log4j.message.ParameterizedMessage; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; /** * DataMaskingRewritePolicy * * @author zhouxy * @date 2022/2/23 **/ @Plugin(name = "DataMaskingRewritePolicy", category = "Core", elementType = "rewritePolicy", printObject = true) public class DataMaskingRewritePolicy implements RewritePolicy { //使用静态内部类创建对象,节省空间 private static class StaticDataMaskingRewritePolicy { private static final DataMaskingRewritePolicy dataMaskingRewritePolicy = new DataMaskingRewritePolicy(); } //需要加密的字段配置数组 private static final String[] encryptionKeyArrays = {"password"}; //将数组转换为集合,方便查找 private static final List<String> encryptionKeys = new ArrayList<>(); public DataMaskingRewritePolicy() { if (CollectionUtils.isEmpty(encryptionKeys)) { encryptionKeys.addAll(Arrays.asList(encryptionKeyArrays)); } } /** * 日志修改方法,可以对日志进行过滤,修改 * * @param logEvent * @return */ @Override public LogEvent rewrite(LogEvent logEvent) { if (!(logEvent instanceof Log4jLogEvent)) { return logEvent; } Log4jLogEvent log4jLogEvent = (Log4jLogEvent) logEvent; Message message = log4jLogEvent.getMessage(); if (!(message instanceof ParameterizedMessage)) { return logEvent; } ParameterizedMessage parameterizedMessage = (ParameterizedMessage) message; Object[] params = parameterizedMessage.getParameters(); if (params == null || params.length <= 0) { return logEvent; } Object[] newParams = new Object[params.length]; for (int i = 0; i < params.length; i++) { try { JSONObject jsonObject = JSON.parseObject(params[i].toString()); //处理json格式的日志 newParams[i] = encryption(jsonObject, encryptionKeys); } catch (Exception e) { newParams[i] = params[i]; } } ParameterizedMessage m = new ParameterizedMessage(parameterizedMessage.getFormat(), newParams, parameterizedMessage.getThrowable()); Log4jLogEvent.Builder builder = log4jLogEvent.asBuilder().setMessage(m); return builder.build(); } /** * 单例模式创建(静态内部类模式) * * @return */ @PluginFactory public static DataMaskingRewritePolicy createPolicy() { return StaticDataMaskingRewritePolicy.dataMaskingRewritePolicy; } /** * 处理日志,递归获取值 * * @Author zhouxy */ private Object encryption(Object object, List<String> encryptionKeys) { String jsonString = JSON.toJSONString(object); if (object instanceof JSONObject) { JSONObject json = JSON.parseObject(jsonString); boolean isContain = encryptionKeys.stream().anyMatch(key -> StringUtils.contains(jsonString, key)); if (isContain) { //判断当前字符串中有没有key值 Set<String> keys = json.keySet(); keys.forEach(key -> { boolean result = encryptionKeys.stream().anyMatch(ekey -> Objects.equal(ekey, key)); if (result) { String value = json.getString(key); //加密 json.put(key, "****"); } else { json.put(key, encryption(json.get(key), encryptionKeys)); } }); } return json; } else if (object instanceof JSONArray) { JSONArray jsonArray = JSON.parseArray(jsonString); for (int i = 0; i < jsonArray.size(); i++) { JSONObject jsonObject = jsonArray.getJSONObject(i); //转换 jsonArray.set(i, encryption(jsonObject, encryptionKeys)); } return jsonArray; } return object; } }
2.log4j2.xml配置,将重写类配置到log4j2.xml中,使用<Rewrite>标签。
<?xml version="1.0" encoding="UTF-8"?> <Configuration status="WARN" monitorInterval="30"> <Properties> <!-- 日志文件存放目录 --> ······ </Properties> <Appenders> <!-- 控制台输出日志 --> <Console name="Console" target="SYSTEM_OUT" follow="true"> ······ </Console> <!-- 配置重写日志 --> <Rewrite name="rewrite"> <DataMaskingRewritePolicy/> <AppenderRef ref="Console"/> <!-- 将catalina日志重写 --> <AppenderRef ref="Catalina"/> </Rewrite> <!-- Catalina日志 --> <RollingFile name="Catalina" fileName="${LOG_HOME}/catalina/catalina.log" filePattern="${LOG_HOME}/catalina/catalina-%d{yyyy-MM-dd}-%i.log.gz"> ······ </RollingFile> </Appenders> <Loggers> ······ <Root level="INFO"> <AppenderRef ref="Catalina"/> <!-- 打印重写日志 --> <AppenderRef ref="rewrite"/> </Root> </Loggers> </Configuration>
过程中遇到的问题:
1.在自己的项目中搭建日志脱敏的代码,测试没有问题。但是将该日志脱敏代码放到公司的项目中,报如下错误:
2022-02-23 18:33:24,425 main ERROR Rewrite contains an invalid element or attribute "DataMaskingRewritePolicy"
日志脱敏代码创建不成功。没办法实现日志脱敏功能。经过排查,发现是maven依赖与当前类有冲突:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${java.version}</source> <target>${java.version}</target> <!--annotationProcessorPaths这个引入注释掉之后,DataMaskingRewritePolicy就可以创建成功--> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </path> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok-mapstruct-binding</artifactId> <version>0.2.0</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin>
原因的话,我估计是lombok中也有涉及到日志的功能,可能版本不对应,导致不支持RewriteAppender类
结论:
在研究日志脱敏技术过程中,网上的文献比较少,只能依靠看官网代码。因为之前很少去研究log4j2的技术,在看日志脱敏的技术时比较难理解思想,很难去实施。因此在感觉自己很难理解的时候,我去巩固了一下log4j2技术的基础,理解了它的思想。在后来去配置以及排查问题的时候,这些基础思想起了很大的作用。
但是在我陆陆续续研究了两三个星期了之后,由于我们公司的日志系统是一个公共的系统,没办法针对不同的系统做日志脱敏的配置。最后只能放弃了,这是很遗憾的事情。回想这个过程,可能刚开始的方向没把握好,如果只是对密码password进行日志脱敏的话,可以让前端传过来加密密码就可以了。但是对于B端对B端来说,是有参考意义的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人