spring boot 日志脱敏-logback(二)

spring boot 日志脱敏-logback(二)

之前有写过一个 通过 重写 logback 中的 encoder 来实现, 感觉那个不太方便 ,并且, 如果集成了 skywalking 的 tid 功能后,就不能使用了,于是就使用了另外一种来实现

通过 重写 MessageConverter 的 convert 方法来实现 日志脱敏

SensitiveLogConverter 实现

public class SensitiveLogConverter extends MessageConverter {

    private Map<String, Pattern> patternsMap = new LinkedHashMap<>();
    private static List<String> extPattern = new ArrayList<>();
    private volatile boolean extInit = false;

    public SensitiveLogConverter() {
        if (patternsMap.isEmpty()) {
            loadPatterns();
        }
    }

    public static void setExtPattern(List<String> input){
        extPattern = input;
    }

    private void loadPatterns() {
        for (PatternType patternType : PatternType.values()) {
            Pattern pattern = Pattern.compile(patternType.getRegex());
            patternsMap.put(patternType.getDescription(), pattern);
        }
    }



    @Override
    public String convert(ILoggingEvent event) {
        if (!extInit){
            //todo 会初始化多次??? why ??
            if (!CollectionUtils.isEmpty(extPattern)) {
                extPattern.stream().forEach(str -> {
                    if (!patternsMap.containsKey(str)) {
                        Pattern pattern = Pattern.compile(str);
                        patternsMap.put(str, pattern);
                    }
                });
            }
            extInit=true;
        }
        return process(event.getFormattedMessage());
    }


    public String process(String message) {
        for (String key : patternsMap.keySet()) {
            // 1.生成matcher
            Pattern pattern = patternsMap.get(key);
            Matcher matcher = pattern.matcher(message);

            // 2.获取匹配的信息
            Set<String> matches = extractMatchesByType(matcher);
            if (CollectionUtil.isEmpty(matches)) {
                continue;
            }

            //如果是匹配了日期,则认为是 流水号, 不做处理
            if (!CollectionUtil.isEmpty(matches)) {
                message = maskByType(key, message, matches);
            }
        }
        return message;
    }


    /**
     * 根据正则类型来做相应的提取
     *
     * @param matcher
     * @return
     */
    private Set<String> extractMatchesByType(Matcher matcher) {
        // 邮箱、电话、银行卡、身份证都是通过如下方法进行提取匹配的字符串
        return extractDefault(matcher);

    }

    /**
     * 1.提取匹配的所有字符串中某一个分组
     * group(0):表示不分组,整个表达式的值
     * group(i),i>0:表示某一个分组的值
     * <p>
     * 2.使用Set进行去重
     *
     * @param matcher
     * @return
     */
    private Set<String> extractDefault(Matcher matcher) {
        Set<String> matches = new HashSet<String>();
        int count = matcher.groupCount();
        while (matcher.find()) {
            if (count == 0) {
                matches.add(matcher.group());
                continue;
            }
            for (int i = 1; i <= count; i++) {
                String match = matcher.group(i);
                if (null != match) {
                    matches.add(match);
                }
            }
        }

        return matches;
    }


    /**
     * 根据不同类型敏感信息做相应的处理
     *
     * @param key
     * @param message
     * @return
     */
    protected String maskByType(String key, String message, Set<String> matchs) {
        return maskNumber(message, matchs);
    }

    /**
     * 掩盖数字类型信息
     *
     * @param message
     * @param matches
     * @return
     */
    private String maskNumber(String message, Set<String> matches) {
        for (String match : matches) {
            // 1.处理获取的字符
            String matchProcess = doMask(match);
            // 2.String的替换
            message = message.replace(match, matchProcess);
        }
        return message;
    }

    private String doMask(String match) {
        String matchProcess = null;
        if (match.length() > 6) {
            matchProcess = DesensitizationUtil.desValue(match, 1, 4, "*");
        } else {
            matchProcess = DesensitizationUtil.desValue(match, 1, 1, "*");
        }
        return matchProcess;
    }

    /**
     * 定义敏感信息类型
     */
    private enum PatternType {


        // 1.手机号共11位,模式为: 13xxx,,14xxx,15xxx,17xxx,18xx
        PHONE_NUMBER("手机号", "[^\\d](1[3-9]\\d{9})[^\\d]"),
        // 2.银行卡号,包含16位和19位
        BANK_CARD("银行卡", "[^\\d](\\d{16})[^\\d]|[^\\d](\\d{19})[^\\d]"),
        // 3.邮箱
        // EMAIL("邮箱", "[A-Za-z_0-9]{1,64}@[A-Za-z1-9_-]+.[A-Za-z]{2,10}"),
        EMAIL("邮箱", "((\\w+)@\\w+\\.\\w+)"),
        // 4. 15位(全为数字位)或者18位身份证(17位位数字位,最后一位位校验位)
        ID_CARD("身份证", "[^\\d](\\d{15})[^\\d]|[^\\d](\\d{18})[^\\d]|[^\\d](\\d{17}X)"),
        // 5. 13位(全为数字位)黄金交易编号
        TRANSACTION_CODE("黄金编号", "[^\\d](131\\d{10})[^\\d]");

        private String description;
        private String regex;

        private PatternType(String description, String regex) {
            this.description = description;
            this.regex = regex;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getRegex() {
            return regex;
        }

        public void setRegex(String regex) {
            this.regex = regex;
        }
    }
}

在 logback.xml 的 configuration 节点下(紧挨着 configuration节点,在其他节点下没用), 加一个 conversionRule 节点 <conversionRule conversionWord="m" converterClass="com.xxx.log.SensitiveLogConverter" />

<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="true" scanPeriod="30 seconds">
    <property name="LOG_HOME" value="logs"/>
    <contextName>${CONTEXT_NAME}</contextName>

    <!--0. 日志格式和颜色渲染 -->
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
    <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
    <conversionRule conversionWord="m" converterClass="com.xxx.log.SensitiveLogConverter" />
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:
    -%clr(%d{yyyy-MM-dd HH:mm:ss.SSS})[%tid]{faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} 
    %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
</configuration>

这样就可以实现了,而且 也做了扩展, 如果要传入其他类型的正则表达式,则可以通过

@PostConstruct
public void initLog(){
    SensitiveLogConverter.setExtPattern(Arrays.asList("正则表达式"));
}

good luck!!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值