spring boot 日志脱敏-logback(二)
之前有写过一个 通过 重写 logback 中的 encoder 来实现, 感觉那个不太方便 ,并且, 如果集成了 skywalking 的 tid 功能后,就不能使用了,于是就使用了另外一种来实现。在 logback.xml 的 configuration 节点下(紧挨着 configuration节点,在其他节点下没用), 加一个。这样就可以实现了,而且 也做了扩展, 如果要传入其他类型的正则
·
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!!
更多推荐
已为社区贡献21条内容
所有评论(0)