1. 程式人生 > >Java中關於使用logback做日誌脫敏

Java中關於使用logback做日誌脫敏

最近在研究如何使用logback實現日誌脫敏的工作,網上各種查,各種找,終於找到了解決的辦法。其實原理知道,就是想找一個最簡便的方法而已。那今天咱們就來聊聊如何用Logback建立自定義格式轉換符來實現日誌脫敏**

  • 脫敏類檔案
  • 配置檔案

脫敏類檔案

資料脫敏是指對某些敏感資訊通過脫敏規則進行資料的變形,實現敏感隱私資料的可靠保護。 —— [ 百度百科 ]

本文使用最簡單的脫敏方式進行資料脫敏列印,規則如下:

| 引數 | 脫敏前 | 脫敏後 |

| ——– | ——– | ——– |

| 姓名 | 李麗麗 | 李** |

| 手機號 | 13898701234 | 138****1234 |

| 身份證號 | 111111111111115762 | **************5762 |

| 銀行卡號 | 6222600890987671234 | 6222600********1234 |

具體程式碼

首先定義類:SensitiveDataConverter 繼承父類:MessageConverter


import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;

import java.util.regex.Matcher;
import
java.util.regex.Pattern; /** * 敏感資訊脫敏處理 * @author AAA */ public class SensitiveDataConverter extends MessageConverter { @Override public String convert(ILoggingEvent event){ // 獲取原始日誌 String oriLogMsg = event.getFormattedMessage(); // 獲取脫敏後的日誌 String afterLogMsg
= invokeMsg(oriLogMsg); return afterLogMsg; } /** * 日誌脫敏開關 */ private static String converterCanRun = "true"; /** * 日誌脫敏關鍵字 */ private static String sensitiveDataKeys = "idcard,realname,bankcard,mobile"; /** * 處理日誌字串,返回脫敏後的字串 * @param msg * @return */ public String invokeMsg(final String oriMsg){ String tempMsg = oriMsg; if("true".equals(converterCanRun)){ // 處理字串 if(sensitiveDataKeys != null && sensitiveDataKeys.length() > 0){ String[] keysArray = sensitiveDataKeys.split(","); for(String key: keysArray){ int index= -1; do{ index = tempMsg.indexOf(key, index+1); if(index != -1){ // 判斷key是否為單詞字元 if(isWordChar(tempMsg, key, index)){ continue; } // 尋找值的開始位置 int valueStart = getValueStartIndex(tempMsg, index + key.length()); // 查詢值的結束位置(逗號,分號)........................ int valueEnd = getValuEndEIndex(tempMsg, valueStart); // 對獲取的值進行脫敏 String subStr = tempMsg.substring(valueStart, valueEnd); subStr = tuomin(subStr, key); /////////////////////////// tempMsg = tempMsg.substring(0,valueStart) + subStr + tempMsg.substring(valueEnd); } }while(index != -1); } } } return tempMsg; } private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]"); /** * 判斷從字串msg獲取的key值是否為單詞 , indexkeymsg中的索引值 * @return */ private boolean isWordChar(String msg, String key, int index){ // 必須確定key是一個單詞............................ if(index != 0){ // 判斷key前面一個字元 char preCh = msg.charAt(index-1); Matcher match = pattern.matcher(preCh + ""); if(match.matches()){ return true; } } // 判斷key後面一個字元 char nextCh = msg.charAt(index + key.length()); Matcher match = pattern.matcher(nextCh + ""); if(match.matches()){ return true; } return false; } public static void main(String[] args) { String tempMsg = "{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=130333198901192762, realname=%E5%BE%90%E5%BD%A6%E5%A8%9C, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=13210141605, bankcard=6226430106137525}"; String tempMsg1 = "{\"reason\":
\"成功 \",\"result\":{\"jobid\":\"JH2131171027170837443588J6\",\"realname\":\"李哪娜\",\"bankcard\":\"6226430106137525\",\"idcard\":\"130333198901192762\",\"mobile\":\"13210141605\",\"res\":\"1\",\"message\":\"驗證成功\"},\"error_code\":0}"; SensitiveDataConverter sc = new SensitiveDataConverter(); System.out.println(sc.invokeMsg(tempMsg)); System.out.println(sc.invokeMsg(tempMsg1)); } /** * 獲取value值的開始位置 * @param msg 要查詢的字串 * @param valueStart 查詢的開始位置 * @return */ private int getValueStartIndex(String msg, int valueStart ){ // 尋找值的開始位置................................. do{ char ch = msg.charAt(valueStart); if(ch == ':' || ch == '='){ // key與 value的分隔符 valueStart ++; ch = msg.charAt(valueStart); if(ch == '"'){ valueStart ++; } break; // 找到值的開始位置 }else{ valueStart ++; } }while(true); return valueStart; } /** * 獲取value值的結束位置 * @return */ private int getValuEndEIndex(String msg,int valueEnd){ do{ if(valueEnd == msg.length()){ break; } char ch = msg.charAt(valueEnd); if(ch == '"'){ // 引號時,判斷下一個值是結束,分號還是逗號決定是否為值的結束 if(valueEnd+1 == msg.length()){ break; } char nextCh = msg.charAt(valueEnd+1); if(nextCh ==';' || nextCh == ','){ // 去掉前面的 \ 處理這種形式的資料 while(valueEnd>0 ){ char preCh = msg.charAt(valueEnd-1); if(preCh != '\\'){ break; } valueEnd--; } break; }else{ valueEnd ++; } }else if (ch ==';' || ch == ',' || ch == '}'){ break; }else{ valueEnd ++; } }while(true); return valueEnd; } private String tuomin(String submsg, String key){ // idcard:身份證號, realname:姓名, bankcard:銀行卡號, mobile:手機號 if("idcard".equals(key)){ return SensitiveInfoUtils.idCardNum(submsg); } if("realname".equals(key)){ return SensitiveInfoUtils.chineseName(submsg); } if("bankcard".equals(key)){ return SensitiveInfoUtils.bankCard(submsg); } if("mobile".equals(key)){ return SensitiveInfoUtils.mobilePhone(submsg); } return ""; } }

import org.apache.commons.lang.StringUtils;

public class SensitiveInfoUtils {

    /**
     * [姓名] 只顯示第一個漢字,其他隱藏為星號<例子:李**>
     * 
     * @param fullName
     * @return
     */
    public static String chineseName(String fullName) {
        if (StringUtils.isBlank(fullName)) {
            return "";
        }
        String name = StringUtils.left(fullName, 1);
        return StringUtils.rightPad(name, StringUtils.length(fullName), "*");
    }

    /**
     * [身份證號] 顯示最後四位,其他隱藏。共計18位或者15位。<例子:*************5762>
     * 
     * @param idCardNum
     * @return
     */
    public static String idCardNum(String idCardNum) {
        if (StringUtils.isBlank(idCardNum)) {
            return "";
        }
        String num = StringUtils.right(idCardNum, 4);
        return StringUtils.leftPad(num, StringUtils.length(idCardNum), "*");
    }

    /**
     * [手機號碼] 前三位,後四位,其他隱藏<例子:138******1234>
     * 
     * @param num
     * @return
     */
    public static String mobilePhone(String num) {
        if (StringUtils.isBlank(num)) {
            return "";
        }
        return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4),StringUtils.length(num), "*"), "***"));
    }

    /**
     * [銀行卡號] 前六位,後四位,其他用星號隱藏每位1個星號<例子:6222600**********1234>
     * 
     * @param cardNum
     * @return
     */
    public static String bankCard(String cardNum) {
        if (StringUtils.isBlank(cardNum)) {
            return "";
        }
        return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******"));
    }
}

配置檔案

程式碼部分完成之後,我們需要在locback.xml配置檔案中增加一行配置:

<conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule>

詳細如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <!-- Logback預設配置的採用的步驟 -->
    <!-- 1. 嘗試在 classpath 下查詢檔案 logback-test.xml; -->
    <!-- 2. 如果檔案不存在,則查詢檔案 logback.xml; -->
    <!-- 3. 如果兩個檔案都不存在,logback 用 BasicConfigurator 自動對自己進行配置,這會導致記錄輸出到控制檯。 -->

    <!-- 本機環境中只會載入該配置檔案,部署伺服器時請刪除本檔案 -->

    <conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule>
    <!-- 輸出控制檯 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <target>System.out</target>
        <encoder>
            <Pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></Pattern>
        </encoder>
    </appender> 

    <!-- 時間滾動輸出日誌 -->
    <appender name="file—info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>D:/logs/aaaa.log</file>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>D:/logs/aaaa.%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></pattern>
        </encoder>
    </appender>

    <logger name="dfpay-auto" additivity="false">
        <appender-ref ref="INFO_FILE" />
    </logger>

    <root level="INFO">
        <appender-ref ref="console" />
    </root>
</configuration>

然後就可以執行程式碼,輸出結果:

{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=*********2762, realname=%*********************, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=132****1605, bankcard=622643******7525}
{“reason”:”成功 “,”result”:{“jobid”:”JH2131171027170837443588J6”,”realname”:”李**”,”bankcard”:”622643******7525”,”idcard”:”**************2762”,”mobile”:”132****1605”,”res”:”1”,”message”:”驗證成功”},”error_code”:0}

目錄:

結束語

  1. 程式碼很簡單,說白了就是利用logback的Converter,自定義日誌格式轉換符,然後繼承ClassicConverter就可以了 ,當然了還有其他的實現方式,這裡就不多寫了。
  2. 文章有不對的地方歡迎大家指正,共同進步,謝謝!!