Log4j日志脱敏记录一下

2,735 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

  • 在项目上线环境中,需要记录程序运行时产生的各种错误信息、状态信息、调试信息、执行时间记录等日志信息。可以用于查找问题、定位数据等等操作。
  • 日志的具体实现可以有log4j和logback等,这里我们使用SLF4J作为日志系统的实现。

使用SLF4J

  • 使用idea工具可以安装lombok插件,并引入maven包:
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.4</version>
</dependency>
  • 在需要打印日志的类上增加@Slf4j注解,如果查看编译后的class文件,就会发现注解@Slf4j会编码成下列代码:
public static final Logger log = LoggerFactory.getLogger(TestLog.class);
  • 打印日志时使用下列方式打印信息即可:
/**
 * @Author: ZRH
 * @Date: 2021/9/27 17:25
 */
@Slf4j
public class TestLog {

    //public static final Logger log = LoggerFactory.getLogger(TestLog.class);

    public static void main (String[] args) {
        Map<String, String> params = new HashMap<>();
        params.put("Phones", "13668200646,15222222222,15648523699");
        params.put("timestamp", "1231");
        params.put("NAME", "张三");
        params.put("身份证", "150303195208077885,15030319520807158X,15030319520807908X");
        String phone = "15236547789";
        log.info(phone);
        log.info("测试日志电话:{}", phone);
        log.info("测试日志json对象:{}", JSONObject.toJSONString(params));
    }
}
------------------
打印结果如下:
15236547789
测试日志电话:15236547789
测试日志json对象:{"Phones":"13668200646,15222222222,15648523699","身份证":"150303195208077885,15030319520807158X,15030319520807908X","timestamp":"1231","NAME":"张三"}

脱敏操作

  • 这里可以发现,打印的日志把完整的手机号也打印出来了,如果有一些客户敏感信息,如身份证、手机号、银行卡等等信息,是需要进行脱敏的,

  • 特别是如果线上日志是对接了第三方日志系统(如阿里云SLS日志服务等),之前就有用户数据信息没有经过日志脱敏就直接打印输出,导致数据泄漏引发一系列问题 -_-

  • 首先需要自定义实现消息脱敏类SensitiveLogDataConverter并继承MessageConverter类,由logback提供的一个日志消息转化超类。代码如下:

/**
 * 自定义日志脱敏类
 *
 * @Author: ZRH
 * @Date: 2021/9/27 17:22
 */
public final class SensitiveLogDataConverter extends MessageConverter {

    /**
     * 银行卡和姓名正则匹配式
     */
    private static Pattern bankCardPattern = Pattern.compile("(\D)([3-6]\d{3})(\d{8,12})(\d{4})(\D)");
    private static Pattern namePattern = Pattern.compile("([^\u4e00-\u9fa5])([\u4e00-\u9fa5])([\u4e00-\u9fa5]{1,3})([^\u4e00-\u9fa5])");

    /**
     * 手机号和身份证正则匹配式
     */
    private final static Pattern PHONE_PATTERN = Pattern.compile("(?<!\d)(1\d{10})(?!\d)");
    private final static Pattern ID_CARD_PATTERN = Pattern.compile("(?<!\d)(\d{6})([19,20]\d{7})(\d{3}[0-9Xx])(?!\d)");

    @Override
    public String convert (ILoggingEvent event) {
        try {
            final Set<String> list;
            String logMsg = event.getFormattedMessage();
            if (!(list = validateIdCard(logMsg)).isEmpty()) {
                for (String param : list) {
                    logMsg = logMsg.replaceAll(param, SensitiveInfoUtil.desensitizePhoneOrIdCard(param));
                }
            }
            return logMsg;
        } catch (Exception e) {
        }
        return super.convert(event);
    }

    /**
     * 获取日志字符串内容中符合手机号和身份证匹配的内容项
     *
     * @param param
     * @return
     */
    private static Set<String> validateIdCard (String param) {
        Set<String> set = new HashSet<>();
        // 匹配手机号
        Matcher phoneMatcher = PHONE_PATTERN.matcher(param);
        while (phoneMatcher.find()) {
            set.add(phoneMatcher.group());
        }
        // 匹配身份证
        Matcher idCardMatcher = ID_CARD_PATTERN.matcher(param);
        while (idCardMatcher.find()) {
            set.add(idCardMatcher.group());
        }
        return set;
    }
}
  • 然后还需要配置一个自定义日志打印格式文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="1 seconds">

    <contextName>logback</contextName>
    <property name="log.path" value="logs/yx-api-admin.log"/>

    <!-- 指定脱敏类的位置 -->
    <conversionRule conversionWord="msg" converterClass="com.work.web.log.SensitiveLogDataConverter"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %contextName [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <appender name="info" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${log.path}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}.%d{yyyy-MM-dd}.zip</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>%date %level [%thread] %logger{36} [%file : %line] %msg%n
            </pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console"/>
        <appender-ref ref="info"/>
    </root>

</configuration>
  • 还是刚才的示例,查看最终的执行效果:
/**
 * @Author: ZRH
 * @Date: 2021/9/27 17:25
 */
@Slf4j
public class TestLog {

    //public static final Logger log = LoggerFactory.getLogger(TestLog.class);

    public static void main (String[] args) {
        Map<String, String> params = new HashMap<>();
        params.put("Phones", "13668200646,15222222222,15648523699");
        params.put("timestamp", "1231");
        params.put("NAME", "张三");
        params.put("身份证", "150303195208077885,15030319520807158X,15030319520807908X");
        String phone = "15236547789";
        log.info(phone);
        log.info("测试日志电话:{}", phone);
        log.info("测试日志json对象:{}", JSONObject.toJSONString(params));
    }
}
------------------
打印结果如下:
152****7789
测试日志电话:152****7789
测试日志json对象:{"Phones":"136****0646,152****2222,156****3699","身份证":"150***********7885,150***********158X,150***********908X","timestamp":"1231","NAME":"张三"}

最后

  • 虚心学习,共同进步 -_-