欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java基于logback?MessageConverter實(shí)現(xiàn)日志脫敏方案分析

 更新時(shí)間:2024年10月31日 15:40:41   作者:zhibo_lv  
本文介紹了一種日志脫敏方案,即基于logbackMessageConverter和正則匹配的方法,該方法的優(yōu)點(diǎn)是侵入性低,工作量少,只需修改xml配置文件,適用于老項(xiàng)目,感興趣的朋友跟隨小編一起看看吧

背景簡(jiǎn)介

日志脫敏 是常見的安全需求,最近公司也需要將這一塊內(nèi)容進(jìn)行推進(jìn)。看了一圈網(wǎng)上的案例,很少有既輕量又好用的輪子可以讓我直接使用。我一直是反對(duì)過度設(shè)計(jì)的,而同樣我認(rèn)為輪子就應(yīng)該是可以讓人拿去直接用的。所以我準(zhǔn)備分享兩篇博客分別實(shí)現(xiàn)兩種日志脫敏方案。

方案分析

  • logback MessageConverter + 正則匹配本篇博客主要介紹此方法
    • 優(yōu)勢(shì)
      • 侵入性低、工作量極少, 只需要修改xml配置文件,適合老項(xiàng)目
        • 劣勢(shì)
          • 效率低,會(huì)對(duì)每一行日志都進(jìn)行正則匹配檢查,效率受日志長(zhǎng)度影響,日志越長(zhǎng)效率越低,影響日志吞吐量
          • 因基于正則匹配 存在錯(cuò)殺風(fēng)險(xiǎn),部分內(nèi)容難以準(zhǔn)確識(shí)別
  • fastjson Filter + 注解 + 工具類下一篇博客介紹
    • 優(yōu)勢(shì)
      • 性能損耗低、效率高、擴(kuò)展性強(qiáng),精準(zhǔn)脫敏,適合QPS較高日志吞吐量較大的項(xiàng)目。
    • 劣勢(shì)
      • 侵入性較高,需對(duì)所有可能的情況進(jìn)行脫敏判斷
      • 存在漏殺風(fēng)險(xiǎn),全靠開發(fā)控制

其實(shí)還有一種方案,基于 工具類+配置模式
優(yōu)勢(shì)是 工作量低(比注解模式低,比正則匹配模式高),靈活度高,性能也好。但是只適合那些新項(xiàng)目,如果是老項(xiàng)目大家命名不規(guī)范,就很難推動(dòng)整改了。此處不進(jìn)行擴(kuò)展。詳見:項(xiàng)目日志脫敏

logback MessageConverter + 正則匹配

流程圖解

代碼案例

正則匹配日志脫敏工具類

此工具類主要用于實(shí)現(xiàn)依據(jù)配置的正則匹配規(guī)則集,進(jìn)行依次匹配。并提取敏感文本對(duì)其執(zhí)行對(duì)應(yīng)的脫敏策略。大家拿去用可以不做修改

package com.zhibo.log.format;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @Author: Zhibo.lv
 * @Description: 正則匹配日志脫敏工具類
 **/
@Component
public class LogSensitiveUtils {
    // 脫敏日志最大長(zhǎng)度,超出此長(zhǎng)度的日志放棄脫敏,直接返回
    private static Integer SENSITIVE_LOG_MAX_LENGTH = 10000;
    /**
     * 日志脫敏 獲取規(guī)則集進(jìn)行依次匹配
     * @param content 明文日志文本
     * @return 脫敏后的日志文本
     */
    public static String filterSensitive(String content) {
        try {
            if (StringUtils.isNotBlank(content) && content.length() < SENSITIVE_LOG_MAX_LENGTH) {
                for (Map.Entry<String, List<Pattern>> entry : LogSensitiveConstants.SENSITIVE_SEQUENCE.entrySet()) {
                    content = filter(content, entry.getKey(), entry.getValue());
                }
            }
            return content;
        } catch (Exception e) {
            return content;
        }
    }
    /**
     *
     * @param content   需脫敏字符串
     * @param type      文本類型,依據(jù)類型可以做不同的脫敏方式
     * @param patterns  該方式下需匹配的正則
     * @return
     *
     */
    public static String filter(String content, String type, List<Pattern> patterns) {
        for (Pattern pattern : patterns) {
            Matcher matcher = pattern.matcher(content);
            StringBuffer sb = new StringBuffer();
            while (matcher.find()) {
                matcher.appendReplacement(sb, Matcher.quoteReplacement(baseSensitive(matcher.group(), type)));
            }
            matcher.appendTail(sb);
            content = sb.toString();
        }
        return content;
    }
    /**
     * 依據(jù)正則抓去的文本執(zhí)行對(duì)應(yīng)的脫敏策略
     * @param str 待脫敏的字符串
     * @return
     */
    private static String baseSensitive(String str, String type) {
        if (StringUtils.isBlank(str)) {
            return StringUtils.EMPTY;
        }
		//通過工廠獲取對(duì)應(yīng)類型的脫敏類執(zhí)行脫敏方法
        return SensitiveStrategyBuiltInUtil.getStrategy(type).des(str);
    }
}

正則匹配日志脫敏常量

此工具類主要是進(jìn)行配置需要脫敏的文本的正則。需要大家依據(jù)業(yè)務(wù)調(diào)整或新增

package com.zhibo.log.format;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;
/**
 * @Author: Zhibo
 * @Description: 正則匹配日志脫敏常量
 **/
public class LogSensitiveConstants {
    /**
     * 過濾先后順序:身份證 -> 手機(jī)號(hào)
     * 順序原因:避免部分業(yè)務(wù)需求出現(xiàn)可能同時(shí)滿足多個(gè)正則規(guī)則的文本,大家可以優(yōu)先提取更長(zhǎng)的、更復(fù)雜的文本。后處理簡(jiǎn)單的
     */
    public static final Map<String,List<Pattern>> SENSITIVE_SEQUENCE = new TreeMap<String, List<Pattern>>();
    /**
     * 手機(jī)號(hào)匹配規(guī)則集,支持配置多個(gè)正則規(guī)則
     */
    public static final List<Pattern> SENSITIVE_PHONE_KEY = new ArrayList<Pattern>(1);
    /**
     * 身份證號(hào)碼匹配規(guī)則集,支持配置多個(gè)正則規(guī)則
     */
    public static final List<Pattern> SENSITIVE_ID_NO_KEY = new ArrayList<Pattern>(1);
    /**
     * 手機(jī)號(hào)正則匹配,11位1開頭數(shù)字
     * 瞻前顧后:校驗(yàn)符合要求的文本前后均不能為數(shù)字 避免誤匹配
     */
    public static final String PHONE_REGEX = "(?<!\\d)[1][3-9][0-9]{9}(?!\\d)";
    /**
     * 身份證號(hào)正則匹配 18位數(shù)版本
     * 15位數(shù)的身份證號(hào)碼暫不考慮,如果需要自行新增下方正則加入 SENSITIVE_ID_NO_KEY 中
     * (?<!\d)([1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3})(?!\d)
     * 瞻前顧后:校驗(yàn)符合要求的文本前后均不能為數(shù)字 避免誤匹配
     */
    public static final String ID_NO_REGEX = "(?<!\\d)([1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X))(?!\\d)";
    static {
        SENSITIVE_ID_NO_KEY.add(Pattern.compile(ID_NO_REGEX));
        SENSITIVE_PHONE_KEY.add(Pattern.compile(PHONE_REGEX));
    }
	// 脫敏替代字符
	public static final char STAR = '*';
	// 手機(jī)號(hào)類型脫敏替代字符
    public static final String PHONE_MASK = "****";
    /** 手機(jī)號(hào)碼脫敏策略 */
    public static final String STRATEGY_PHONE = "strategyPhone";
    /** 身份證號(hào)碼脫敏策略 */
    public static final String STRATEGY_ID_NO = "strategyIdNo";
    static {
    	//將每一個(gè)規(guī)則集綁定一個(gè)對(duì)應(yīng)的類型
        SENSITIVE_SEQUENCE.put(STRATEGY_ID_NO, SENSITIVE_ID_NO_KEY);
        SENSITIVE_SEQUENCE.put(STRATEGY_PHONE, SENSITIVE_PHONE_KEY);
    }
    private LogSensitiveConstants() {
    }
}

脫敏策略代碼

定義文本脫敏接口 IStrategy

package com.zhibo.log.sensitive.api;
/**
 * @Author: Zhibo
 * @Description: 脫敏策略
 */
public interface IStrategy {
    /**
     * 脫敏
     * @param original 原始內(nèi)容
     * @return 脫敏后的字符串
     */
    String des(final Object original);
}

文本脫敏抽象類,進(jìn)行通用實(shí)現(xiàn) AbstractStringStrategy

package com.zhibo.log.sensitive.core.strategory;
import com.zhibo.log.sensitive.api.IStrategy;
import com.zhibo.log.format.LogSensitiveConstants;
import java.security.MessageDigest;
/**
 * @Author: zhibo
 * @Description: 抽象字符串策略,
 * 				支持在脫敏后的文本后面追加明文的MD5加密串,方便研發(fā)進(jìn)行日志查詢使用
 */
public abstract class AbstractStringStrategy implements IStrategy {
    /**
     * 獲取掩碼之前的長(zhǎng)度
     * @param original 原始
     * @param chars 字符串
     * @return 結(jié)果
     */
    protected abstract int getBeforeMaskLen(Object original, char[] chars);
    /**
     * 獲取掩碼之后的長(zhǎng)度
     * @param original 原始
     * @param chars 字符串
     * @return 結(jié)果
     */
    protected abstract int getAfterMaskLen(Object original, char[] chars);
    /**
     * 針對(duì)固定長(zhǎng)度的加密直接返回脫敏字符串,避免StringBuilder循環(huán)拼接
     * @return 脫敏字符串
     *      如返回null 則通過 {@link AbstractStringStrategy#getBeforeMaskLen(Object, char[])} 與 {@link AbstractStringStrategy#getAfterMaskLen(Object, char[])}
     *      進(jìn)行截取字符串
     */
    protected String getMask(){
        return null;
    }
    /**
     * 是否需要拼接MD5密文方便日志查詢。
     * @return false : 不拼接(默認(rèn))
     *          true :  拼接密文 用于日志查詢 格式  [MD5]
     */
    protected Boolean addMD5(){
        return false;
    }
    @Override
    public String des(Object original) {
        if(original == null) {
            return null;
        }
        String strValue = original.toString();
        char[] chars = strValue.toCharArray();
        int beforeMaskLen = getBeforeMaskLen(original, chars);
        int afterMaskLen = getAfterMaskLen(original, chars);
        //范圍糾正
        int maxLen = chars.length;
        beforeMaskLen = Math.min(beforeMaskLen, maxLen);
        afterMaskLen = Math.min(afterMaskLen, maxLen);
        StringBuilder stringBuilder = new StringBuilder();
        //獲取明文前綴
        if(beforeMaskLen > 0) {
            stringBuilder.append(chars, 0, beforeMaskLen);
        }
        //獲取脫敏字符串
        String mask = getMask();
        if (null == mask){//如未指定脫敏字符串則按規(guī)則循環(huán)拼接
            // 中間使用掩碼
            for(int i = beforeMaskLen; i < chars.length - afterMaskLen; i++) {
                stringBuilder.append(LogSensitiveConstants.STAR);
            }
        }else {
            stringBuilder.append(mask);
        }
        //獲取明文后綴
        if(afterMaskLen > 0) {
            stringBuilder.append(chars, chars.length - afterMaskLen, afterMaskLen);
        }
        if (addMD5()){
            addMD5(strValue,stringBuilder);
        }
        return stringBuilder.toString();
    }
    // MD5加密
	private void addMD5(String originalString,StringBuilder stringBuilder) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(originalString.getBytes());
            byte[] digest = md.digest();
            stringBuilder.append("[");
            for (byte b : digest) {
                stringBuilder.append(String.format("%02x", b));
            }
            stringBuilder.append("]");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

身份證脫敏策略實(shí)現(xiàn) StrategyIdNo

package com.zhibo.log.sensitive.core.strategory;
/**
 * @Author: Zhibo
 * @Description: 身份證號(hào)脫敏
 * 脫敏規(guī)則:保留前6 后4 位,其它由星號(hào)替換
 */
public class StrategyIdNo extends AbstractStringStrategy {
    @Override
    protected int getBeforeMaskLen(Object original, char[] chars) {
        return 6;
    }
    @Override
    protected int getAfterMaskLen(Object original, char[] chars) {
        return 4;
    }
}

手機(jī)號(hào)碼脫敏策略實(shí)現(xiàn) StrategyPhone

package com.zhibo.log.sensitive.core.strategory;
import com.zhibo.log.format.LogSensitiveConstants;
/**
 * @Author: zhibo
 * @Description: 手機(jī)號(hào)脫敏
 * 脫敏規(guī)則:186****8567[MD5]
 */
public class StrategyPhone extends AbstractStringStrategy {
    @Override
    protected int getBeforeMaskLen(Object original, char[] chars) {
        return 3;
    }
    @Override
    protected int getAfterMaskLen(Object original, char[] chars) {
        return 4;
    }
    @Override
    protected String getMask() {
        return LogSensitiveConstants.PHONE_MASK;
    }
    @Override
    protected Boolean addMD5(){
        return true;
    }
}

logback 消息轉(zhuǎn)換器實(shí)現(xiàn)

最關(guān)鍵的方法來啦

package com.zhibo.log.format;
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
/**
 * @Author: Zhibo
 * @Description: 日志脫敏轉(zhuǎn)換器
 **/
public class SensitiveConverter extends MessageConverter {
    @Override
    public String convert(ILoggingEvent event){
        // 獲取原始日志
        String requestLogMsg = super.convert(event);
        // 執(zhí)行日志脫敏
        return LogSensitiveUtils.filterSensitive(requestLogMsg);
    }
    public SensitiveConverter() {
    }
}

自此我們的工具包也就完成了,業(yè)務(wù)系統(tǒng)需要使用此工具只需要修改resources目錄下的logback.xml配置。

<!-- 新增或修改原有消息轉(zhuǎn)換器為SensitiveConverter -->
<conversionRule conversionWord="msgToo" converterClass="com.zhibo.log.format.SensitiveConverter" />

并將文件輸出日志的消息內(nèi)容替換為指定消息轉(zhuǎn)換器的 conversionWord

脫敏效果展示

有請(qǐng)?zhí)崾?/h3>

注:此方法對(duì)日志吞吐量存在影響,由于正則需要循環(huán)匹配整個(gè)日志文本,所以正則規(guī)則越多,日志文本越長(zhǎng),耗時(shí)越長(zhǎng)。如您的應(yīng)用程序?qū)θ罩就掏铝恳筝^高且存在大量超長(zhǎng)日志文本請(qǐng)壓測(cè)后使用。

如配置了logback的異步打印,且設(shè)置了允許日志丟棄,在壓測(cè)中可能出現(xiàn)因線程池與等待隊(duì)列均被占滿而導(dǎo)致日志丟失情況。下面是我的問題復(fù)盤:

logback日志異步打印配置如下

<appender name="ASYNC-FILE" class="ch.qos.logback.classic.AsyncAppender">
		<neverBlock>true</neverBlock><!-- 非阻塞方式運(yùn)行 如隊(duì)列滿就開始丟棄日志 -->
		<queueSize>1024</queueSize><!-- 等待隊(duì)列大小 -->
		<discardingThreshold>0</discardingThreshold><!-- 日志隊(duì)列深度,配置0 隊(duì)列滿后丟棄最老的日志 -->
		<appender-ref ref="FILE"/>
</appender>

以上配置 為logback線程池工作配置,默認(rèn)線程池 線程數(shù)為 10個(gè),最大隊(duì)列長(zhǎng)度為1024個(gè)。
意味著如果日志產(chǎn)生的速度超過10個(gè)線程工作處理日志的速度,則無法處理的日志會(huì)被寫入BlockingQueue 隊(duì)列,當(dāng)隊(duì)列滿了之后就會(huì)導(dǎo)致日志丟失的情況。

到此這篇關(guān)于Java基于logback MessageConverter實(shí)現(xiàn)日志脫敏的文章就介紹到這了,更多相關(guān)java logback MessageConverter日志脫敏內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論