本文描述了一种使用logback进行项目中打印的日志进行脱敏的一种处理方法,通过底层消息转换,字段过滤,以及数据加密,对象lazy化多种方法来完成整个实现体系。
参考文章:https://blog.csdn.net/lrcxl/article/details/78723756 关于实现log4j2日志脱敏的一种方案
整个实现分为以下4个步骤
以下实现中修改了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); } }
之所以使用自定义的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方法可以实现多种不同的脱敏策略,如 转换为 *** 或者 直接字符串加密处理等.
针对在业务中的数据脱敏处理,一直简单的方法就是针对整个数据作加密化输出,同时后端管理提供一个解密工具,以方便将线上日志解密化。 这里面的实现可以采用AES加密,同时项目内部约定一个统一的密钥,即可以通过这种方法保证数据的安全性。即安全通过密钥来保证。
考虑到在实际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化