日常记录-logback脱敏
logback添加如下配置:
<!-- 自定义日志转换规则 -->
<conversionRule conversionWord="sensitived" converterClass="cn.framework.sensitive.SensitiveConverter" />
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class SensitiveConverter extends MessageConverter {
/**
* 日志脱敏开关
*/
private static boolean converterCanRun = true;
private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]");
@Override
public String convert(ILoggingEvent event) {
// 原始日志
String oriLogMsg = event.getFormattedMessage();
try {
// 日志脱敏处理
return invokeMsg(oriLogMsg);
} catch (Exception e) {
return oriLogMsg;
}
}
/**
* 日志脱敏处理
*
* @return 脱敏后的日志
*/
private String invokeMsg(final String oriMsg) {
if (!converterCanRun) {
return oriMsg;
}
if (StringUtils.isBlank(oriMsg) || oriMsg.length() > 5000) {//太长就不脱敏了,处理慢,存在风险
return oriMsg;
}
String tempMsg = oriMsg;
// 处理字符串
List<SensitiveFieldEnum> sensitiveFieldEnumList = Arrays.stream(SensitiveFieldEnum.values()).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(sensitiveFieldEnumList)) {
for (SensitiveFieldEnum fieldEnum : sensitiveFieldEnumList) {
String fieldName = fieldEnum.getFieldName();
int index = -1;
do {
index = StringUtils.indexOfIgnoreCase(tempMsg, fieldName, index + 1);
if (index != -1) {
// 判断key是否为单词字符
if (isWordChar(tempMsg, fieldName, index)) {
continue;
}
// 寻找值的开始位置
int valueStart = getValueStartIndex(tempMsg, index + fieldName.length());
// 查找值的结束位置........................
int valueEnd = getValuEndIndex(tempMsg, valueStart);
// 对获取的值进行脱敏
String subStr = tempMsg.substring(valueStart, valueEnd);
//脱敏处理
subStr = fieldEnum.desensitizationProcessing(subStr);
tempMsg = tempMsg.substring(0, valueStart) + subStr + tempMsg.substring(valueEnd);
}
} while (index != -1);
}
}
return tempMsg;
}
/**
* 判断从字符串msg获取的key值是否为单词 , index为key在msg中的索引值
*/
private boolean isWordChar(String msg, String key, int index) {
// 必须确定key是一个单词............................
if (index != 0) { // 判断key前面一个字符
char preCh = msg.charAt(index - 1);
Matcher match = pattern.matcher(preCh + "");
if (match.matches()) {
return true;
}
}
// 判断key后面一个字符
char nextCh = msg.charAt(index + key.length());
Matcher match = pattern.matcher(nextCh + "");
return match.matches();
}
/**
* 获取value值的开始位置
*
* @param msg 要查找的字符串
* @param valueStart 查找的开始位置
*/
private int getValueStartIndex(String msg, int valueStart) {
// 寻找值的开始位置.................................
do {
char ch = msg.charAt(valueStart);
if (ch == ':' || ch == ':' || ch == '=') { // key与 value的分隔符
valueStart++;
ch = msg.charAt(valueStart);
if (ch == '"' || ch == '\'') {
valueStart++;
}
break; // 找到值的开始位置
} else {
valueStart++;
}
} while (true);
return valueStart;
}
/**
* 获取value值的结束位置
*/
private int getValuEndIndex(String msg, int valueEnd) {
do {
if (valueEnd == msg.length()) {
break;
}
char ch = msg.charAt(valueEnd);
if (ch == '"' || ch == '\'') { // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束
if (valueEnd + 1 == msg.length()) {
break;
}
char nextCh = msg.charAt(valueEnd + 1);
if (nextCh == ';' || nextCh == ',') {
// 去掉前面的 \ 处理这种形式的数据
while (valueEnd > 0) {
char preCh = msg.charAt(valueEnd - 1);
if (preCh != '\\') {
break;
}
valueEnd--;
}
break;
} else {
valueEnd++;
}
} else if (ch == ';' || ch == ';' || ch == ',' || ch == ',' || ch == '}') {
break;
} else {
valueEnd++;
}
} while (true);
return valueEnd;
}
}
public enum SensitiveFieldEnum {
//银行卡号
BANKACCOUNT("BANKACCOUNT", "银行卡号"),
NEWBANKACCOUNT("NEWBANKACCOUNT", "新卡号")
;
private String fieldName;//字段名
private String fieldDescribe;//字段描述
public String getFieldName() {
return fieldName;
}
public String getFieldDescribe() {
return fieldDescribe;
}
SensitiveFieldEnum(String fieldName, String fieldDescribe) {
this.fieldName = fieldName;
this.fieldDescribe = fieldDescribe;
}
public String desensitizationProcessing(String data) {
try {
return DataDesensitizationUtil.generic(data);
} catch (Exception e) {
return data;
}
}
}
import org.apache.commons.lang.StringUtils;
public class DataDesensitizationUtil {
private static final String S = "*";
private static final String SSS = "***";
// 通用脱敏
public static String generic(String data) {
return fullDesensitize(data);
}
// 通用全部脱敏方法,只显示开始和结束的部分
private static String fullDesensitize(String data) {
if (StringUtils.isBlank(data) || StringUtils.equals(data, "null") || StringUtils.equals(data, "<null>")) {
return data;
}
data = data.trim();//去除前后空格
int initLength = data.length();
if (initLength >= 20) {
return data.substring(0, 6) + SSS + data.substring(initLength - 6);
}
if (initLength >= 15) {//银行卡16到19位、身份证15、18位
return data.substring(0, 6) + SSS + data.substring(initLength - 4);
}
if (initLength >= 10) {//手机号11位
return data.substring(0, 3) + SSS + data.substring(initLength - 4);
}
if (initLength >= 8) {
return data.substring(0, 2) + SSS + data.substring(initLength - 4);
}
if (initLength >= 5) {
return SSS + data.substring(initLength - 3);
}
if (initLength >= 3) {//姓名3、4位
return S + data.substring(initLength - 2);
}
if (initLength >= 2) {//姓名2位
return S + data.substring(initLength - 1);
}
return data;
}
}