在logback.xml 中 添加
<conversionRule conversionWord="msg" converterClass="com.common.utils.SensitiveDataConverter"> </conversionRule>
import ch.qos.logback.classic.pattern.MessageConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import com.google.common.collect.Lists; import org.apache.commons.lang3.StringUtils; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 敏感信息脱敏处理 * @author AAA */ public class SensitiveDataConverter extends MessageConverter { /** * 日志脱敏开关 */ private static String converterCanRun = "true"; /** * 日志脱敏关键字 */ private static String sensitiveDataKeys = "idCard,dTel,dMobile,dAddress,jMobile,jAddress,address,phone"; private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]"); //手机号正则匹配 private static final String PHONE_REGEX = "1[3|4|5|6|7|8|9][0-9]\\d{8}"; //身份证号正则匹配 private static final String IDCARD_REGEX = "([1-9]\\d{5}(18|19|20)\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx])|(^[1-9]\\d{5}\\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\\d{3})"; //之所以定义这个参数,完全是处于公司的要求,因为公司要求敏感信息在日志输出时被脱敏后,还能按照一定的规则还原,所以就用这些字母来进行处理了,对于没有这种要求的,就更简单了,直接用*去脱敏就可以了,大家很容易根据现在的代码进行改造,这里不在多讲 private static final String KEY = "axedfqLGpu"; @Override public String convert(ILoggingEvent event){ // 获取原始日志 String oriLogMsg = event.getFormattedMessage(); // 获取脱敏后的日志 String afterLogMsg = invokeMsg(oriLogMsg); // return afterLogMsg; return filterSensitive(afterLogMsg); } /** * 处理日志字符串,返回脱敏后的字符串 * @param: msg * @return */ public static String invokeMsg(final String oriMsg){ String tempMsg = oriMsg; if("true".equals(converterCanRun)){ // 处理字符串 if(sensitiveDataKeys != null && sensitiveDataKeys.length() > 0){ String[] keysArray = sensitiveDataKeys.split(","); for(String key: keysArray){ int index= -1; do{ index = tempMsg.indexOf(key, index+1); if(index != -1){ // 判断key是否为单词字符 if(isWordChar(tempMsg, key, index)){ continue; } // 寻找值的开始位置 int valueStart = getValueStartIndex(tempMsg, index + key.length()); // 查找值的结束位置(逗号,分号)........................ int valueEnd = getValuEndEIndex(tempMsg, valueStart); // 对获取的值进行脱敏 String subStr = tempMsg.substring(valueStart, valueEnd); subStr = tuomin(subStr, key); /// tempMsg = tempMsg.substring(0,valueStart) + subStr + tempMsg.substring(valueEnd); } }while(index != -1); } } } return tempMsg; } /** * 判断从字符串msg获取的key值是否为单词 * index为key在msg中的索引值 * @return */ private static 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 + ""); if(match.matches()){ return true; } return false; } /** * 获取value值的开始位置 * @param msg 要查找的字符串 * @param valueStart 查找的开始位置 * @return */ private static int getValueStartIndex(String msg, int valueStart ){ // 寻找值的开始位置................................. do{ char ch = msg.charAt(valueStart); if(ch == ':' || ch == '='){ // key与 value的分隔符 valueStart ++; ch = msg.charAt(valueStart); if(ch == '"'){ valueStart ++; } break; // 找到值的开始位置 }else{ valueStart ++; } }while(true); return valueStart; } /** * 获取value值的结束位置 * @return */ private static int getValuEndEIndex(String msg,int valueEnd){ do{ if(valueEnd == msg.length()){ break; } char ch = msg.charAt(valueEnd); if(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 == '}'){ break; }else{ valueEnd ++; } }while(true); return valueEnd; } /** * 调用脱敏工具类进行字段日志处理 * @param submsg * @param key * @return */ private static String tuomin(String submsg, String key){ // idcard:身份证号, realname:姓名, bankcard:银行卡号, mobile:手机号,attribute10,shipAddress,detailedAddr:地址 if(Lists.newArrayList("idCard","id_card").contains(key)){ return SensitiveInfoUtils.idCardNum(submsg); } // if(Lists.newArrayList("realname","customerName").equals(key)){ // return SensitiveInfoUtils.chineseName(submsg); // } if(Lists.newArrayList("dTel","","dMobile","","jMobile","","phone").contains(key)){ return SensitiveInfoUtils.mobilePhone(submsg); } if(Lists.newArrayList("dAddress","","jAddress","","address").contains(key)){ return SensitiveInfoUtils.address(submsg,3); } return ""; } // @Override // public String convert(ILoggingEvent event){ // // // 获取原始日志 // String requestLogMsg = event.getFormattedMessage(); // // // 获取返回脱敏后的日志 // return filterSensitive(requestLogMsg); // } /** * 对敏感信息脱敏 * @param content * @return * @author ljh * @date 2019年12月17日 */ public static String filterSensitive(String content) { try { if(StringUtils.isBlank(content)) { return content; } content = filterIdcard(content); return filterMobile(content); }catch(Exception e) { return content; } } /** * [身份证号] 指定展示几位,其他隐藏 。<例子:1101**********5762> * @param num * @return * @author ljh * @date 2019年12月18日 */ private static String filterIdcard(String num){ Pattern pattern = Pattern.compile(IDCARD_REGEX); Matcher matcher = pattern.matcher(num); StringBuffer sb = new StringBuffer() ; while(matcher.find()){ matcher.appendReplacement(sb, baseSensitive(matcher.group(), 4, 4)); } matcher.appendTail(sb) ; return sb.toString(); } /** * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234> * @param num * @return * @author ljh * @date 2019年12月18日 */ private static String filterMobile(String num){ Pattern pattern = Pattern.compile(PHONE_REGEX); Matcher matcher = pattern.matcher(num); StringBuffer sb = new StringBuffer() ; while(matcher.find()){ matcher.appendReplacement(sb, baseSensitive(matcher.group(), 3, 4)) ; } matcher.appendTail(sb) ; return sb.toString(); } /** * 基础脱敏处理 指定起止展示长度 剩余用"KEY"中字符替换 * * @param str 待脱敏的字符串 * @param startLength 开始展示长度 * @param endLength 末尾展示长度 * @return 脱敏后的字符串 */ private static String baseSensitive(String str, int startLength, int endLength) { if (StringUtils.isBlank(str)) { return ""; } String replacement = str.substring(startLength,str.length()-endLength); StringBuffer sb = new StringBuffer(); for(int i=0;i<replacement.length();i++) { char ch; if(replacement.charAt(i)>='0' && replacement.charAt(i)<='9') { ch = KEY.charAt((int)(replacement.charAt(i) - '0')); }else { ch = replacement.charAt(i); } sb.append(ch); } return StringUtils.left(str, startLength).concat(StringUtils.leftPad(StringUtils.right(str, endLength), str.length() - startLength, sb.toString())); } /** * 按"KEY"中字符解密 * @param str * @param startLength * @param endLength * @return * @author ljh * @date 2019年12月18日 */ private static String decrypt(String str, int startLength, int endLength) { if (StringUtils.isBlank(str)) { return ""; } String replacement = str.substring(startLength,str.length()-endLength); StringBuffer sb = new StringBuffer(); for(int i=0;i<replacement.length();i++) { int index = KEY.indexOf(replacement.charAt(i)); if(index != -1) { sb.append(index); }else { sb.append(replacement.charAt(i)); } } return StringUtils.left(str, startLength).concat(StringUtils.leftPad(StringUtils.right(str, endLength), str.length() - startLength, sb.toString())); } public static void main(String[] args) { String tempMsg = "{sign=f88898b2677e62f1ad54b9e330c0a27e, idCard=130333198901192762, realname=%E5%BE%90%E5%BD%A6%E5%A8%9C, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=13210141605, bankcard=6226430106137525}"; String tempMsg1 = "{\"reason\":\"成功 \",\"result\":{\"jobid\":\"JH2131171027170837443588J6\",\"realname\":\"李哪娜\",\"bankcard\":\"6226430106137525\",\"idcard\":\"130333198901192762\",\"mobile\":\"13210141605\",\"res\":\"1\",\"message\":\"验证成功\"},\"error_code\":0}"; String shipAddress ="{shipAddress=宁夏回族自治区.银川市.金凤区.长城中路街道收货人:+卜广龙手机号码:+13992283627}"; String detailedAddr ="{\"attribute10\":\"浙江省.杭州市.桐庐县.桐君街道.收货人: 陈静手机号码: 15168393010所在地区: 浙江省杭州市桐庐县城南街道详细地址: 白云源路1618号银通汽车4楼\"}"; SensitiveDataConverter sc = new SensitiveDataConverter(); System.out.println(sc.invokeMsg(tempMsg)); System.out.println(sc.invokeMsg(tempMsg1)); System.out.println(sc.invokeMsg(shipAddress)); System.out.println(sc.invokeMsg(detailedAddr)); } }