一种使用Logback进行日志脱敏的方法和实现

本文描述了一种使用logback进行项目中打印的日志进行脱敏的一种处理方法,通过底层消息转换,字段过滤,以及数据加密,对象lazy化多种方法来完成整个实现体系。

参考文章:https://blog.csdn.net/lrcxl/article/details/78723756 关于实现log4j2日志脱敏的一种方案

整个实现分为以下4个步骤

  1. 底层消息转换 logback层提供扩展方法,允许注入额外的参数填充实现
  2. 字段过滤 参数toString化工具处理
  3. 数据加密 信息加/解密处理
  4. 对象lazy化, 多种信息转换实现

1 logback层提供扩展方法,允许注入额外的参数填充实现

以下实现中修改了logback-classic的源码, 修改点为 LoggingEvent#getFormattedMessage

我们在logback-classic版本中提供了用于处理消息和参数进行格式化的处理类接口如下

public interface LogbackMessageFormat {
    String format(String message, Object[] argumentArray);
}

其默认实现为 

public class Slf4jLogbackMessageFormat implements LogbackMessageFormat {
    @Override
    public String format(String message, Object[] argumentArray) {
        return MessageFormatter.arrayFormat(message, argumentArray).getMessage();
    }
}

此即为原logback-classic的默认实现。 为了让外部注入额外的实现,提供了一个工厂类,以让外部设置新的处理实现,如下参考所示:

public class LogbackMessageFormatFactory {
    @Getter
    @Setter
    private static LogbackMessageFormat INSTANCE = new Slf4jLogbackMessageFormat();
}

通过此工厂方法,即可设置自己的实现了,一个用于脱敏的参考实现如下

public class SelfImpl implements LogbackMessageFormat {
    @Override
    public String format(String message, Object[] argumentArray) {
        ......
        //以下代码为将参数对象toString化,相应的脱敏即隐藏在此方法中
        for(int i = 0; i < size; i++) {
            argumentArray[i] = ToStringUtils.toString(argumentArray[i]);
        }

        //调用原始格式化信息为字符串
        return StringUtils.format(message, argumentArray);
    }
}

2 参数toString化工具处理

之所以使用自定义的toString接口,主要是为了处理如 InputStream 类的toString问题,同时针对其它一些pojo对象使用json格式来输出日志,这样即避免业务对象的toString方法必须实现为toString的问题.

在这个参数接口中,针对一些业务对象(即业务代码中可以修改的对象),这里使用了标准的Fastjson来将对象输出为json数据。在这个过程中,我们即可以对一些需要进行脱敏的对象作处理。这里引入了一个注解对象 @LogDes,在一个业务对象中,对于需要脱敏的对象,可以使用此注解追加到字段或Getter方法上,如下参考所示

public class User {
    /** 密码 */
    @LogDes
    private String password;
}

当使用fastjson处理此对象时,将读取此注解,如果命中此字段,则进入此字段的具体脱敏化实现处理。这里采用了一个标准的json过滤器来实现处理。参考如下:

public class SelfFilter implements ContextValueFilter {
    @Override
    public Object process(BeanContext context, Object object, String name, Object value) {
        val logdes = findFlag(context);

        return logdes.value().safeString(value);
    }
}

这里的LogDes中通过safeString方法可以实现多种不同的脱敏策略,如 转换为 *** 或者 直接字符串加密处理等.

3 信息加/解密处理

针对在业务中的数据脱敏处理,一直简单的方法就是针对整个数据作加密化输出,同时后端管理提供一个解密工具,以方便将线上日志解密化。 这里面的实现可以采用AES加密,同时项目内部约定一个统一的密钥,即可以通过这种方法保证数据的安全性。即安全通过密钥来保证。

4 对象lazy化, 多种信息转换实现

考虑到在实际logger中,可能还存在对非业务对象,或者 jsonObject 以及三方参数字符串的打印情况,这些参数也要进行脱敏处理。但是因为其不可再使用 @LogDes 来标注,因此需要对原来的参数作一些调整。这里使用了一个lazy的对象来包装实际的参数,一个相应的参数如下

public class LogDecObjSupplier implements Supplier<String> {
    @Setter
    private Object obj;
    private transient String source;

    @Override
    public String get() {
        if(source == null) {
            //进行脱敏化处理
            source = ToStringUtils.toString(obj);
        }

        return source;
    }
}

4.2 性能考虑

在第4步的Supplier中,针对于参数的处理并没有在进行log.info传参前就提前处理,而是直接在实际要进行输出时才会处理,这样可以避免对数据作无谓的脱敏,在性能上可以有一定的提升。当前包装参数的损失就是一次对象构建的成本 ,相比对数据脱敏而实际上不输出日志的情况来说,性能有很大的提升。

总结

整个方案从整个思路和实现上来说,都还是很简单的。主要还是相应数据脱敏的处理点以及如何处理数据脱敏。在具体实现中,以及怎么通过注解来过滤数据,并使用特定的脱敏策略来完成相应的实现。
涉及的技术包括:fastjson序列化过滤器 AES数据加密 Supplier对象lazy化

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/201809130001.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

邮箱地址不会被公开。 必填项已用*标注