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端來說,是有參考意義的。
 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM