public class SensitiveDataConverter extends MessageConverter { /** * 日志脱敏开关 */ private String allowRun; /** * 日志脱敏关键字 */ private Set<SensitiveDataRule> sensitiveDataRules; @Override public String convert(ILoggingEvent event) { if (getAllowRun() == null) { Context context = getContext(); String allowRun = context.getProperty("SensitiveDataAllowRun"); setAllowRun(StringUtils.defaultIfBlank(allowRun, "true")); String[] keys = { "SensitiveDataKeys", "SensitiveDataKeys1", "SensitiveDataKeys2", "SensitiveDataKeys3" }; Set<SensitiveDataRule> sensitiveDataRulesInit = new LinkedHashSet<SensitiveDataRule>(); for (String key : keys) { String dataKeys = context.getProperty(key); List<SensitiveDataRule> sensitiveDataRules = JSON.parseArray(dataKeys, SensitiveDataRule.class); if (CollectionUtils.isNotEmpty(sensitiveDataRules)) { sensitiveDataRulesInit.addAll(sensitiveDataRules); } } setSensitiveDataRules(sensitiveDataRulesInit); } // 获取原始日志 String formattedMessage = event.getFormattedMessage(); if (!"true".equals(allowRun) || sensitiveDataRules == null || sensitiveDataRules.isEmpty() || StringUtils.isEmpty(formattedMessage)) { return formattedMessage; } // 获取脱敏后的日志 return filterMessage(formattedMessage); } /** * 处理日志字符串,返回脱敏后的字符串 * * @param message 原始日志字符串 * @return 脱敏后的字符串 */ public String filterMessage(String message) { StringBuilder temp = new StringBuilder(message); for (SensitiveDataRule sensitiveDataRule : sensitiveDataRules) { String key = sensitiveDataRule.getFieldName(); int index = -1; do { index = temp.indexOf(key, index + 1); if (index != -1) { // 判断key是否为单词字符 if (isWordChar(temp, key, index)) { continue; } // 寻找值的开始位置 int valueStart = getValueStartIndex(temp, index + key.length()); // 查找值的结束位置(逗号,分号) int valueEnd = getValueEndEIndex(temp, valueStart); // 对获取的值进行脱敏 String value = temp.substring(valueStart, valueEnd); String replace = SensitiveProcessUtils.shield(sensitiveDataRule.getFormat(), value); temp.replace(valueStart, valueEnd, replace); } } while (index != -1); } return temp.toString(); } /** * 判断从 {@code msg} 中获取的key值是否为单词, * * @param msg 完整字符串内容 * @param keyword 检查的关键词 * @param index 为key在msg中的索引值 * @return true/false */ private boolean isWordChar(CharSequence msg, String keyword, int index) { // 必须确定key是一个单词 if (index != 0) { // 判断key前面一个字符 char pre = msg.charAt(index - 1); if ((pre > 47 && pre < 58) || (pre > 64 && pre < 91) || (pre > 96 && pre < 123)) { // 0-9 , A-Z, a-z return true; } //避免 "service_name":"ABC"这种被name规则替换, 关键字的前一个字符是双引号、单引号、逗号、空格等时, 才认为后面是value需要替换 if (!" ".equals(String.valueOf(pre)) && !"\"".equals(String.valueOf(pre)) && !"'".equals(String.valueOf(pre)) && !",".equals(String.valueOf(pre)) && !",".equals(String.valueOf(pre))) { return true; } } // 判断key后面一个字符 char next = msg.charAt(index + keyword.length()); if ((next > 47 && next < 58) || (next > 64 && next < 91) || (next > 96 && next < 123)) { // 0-9 , A-Z, a-z return true; } //避免 com.zhongan.mobile.bean {"xxx":"1111"}这种被PHONE_NO规则替换, 关键字的下一个字符是=:" '等时, 才认为后面是value需要替换 if (!"=".equals(String.valueOf(next)) && !":".equals(String.valueOf(next)) && !":".equals(String.valueOf(next)) && !" ".equals(String.valueOf(next)) && !"\"".equals(String.valueOf(next)) && !"'".equals(String.valueOf(next))) { return true; } return false; } /** * 获取value值的开始位置 * * @param msg 要查找的字符串 * @param valueStart 查找的开始位置 * @return value值的开始位置 */ private int getValueStartIndex(CharSequence msg, int valueStart) { // 寻找值的开始位置 do { char c = msg.charAt(valueStart); if (c == ':' || c == '=' || c == ':') { // key与 value的分隔符 valueStart++; c = msg.charAt(valueStart); if (c == '"' || c == ' ') { valueStart++; c = msg.charAt(valueStart); if (c == '"' || c == ' ') { valueStart++; } } // 找到值的开始位置 break; } else { valueStart++; } } while (true); return valueStart; } /** * 获取value值的结束位置 * * @param msg 要查找的字符串 * @param valueEnd 查找的开始位置 * @return value值的结束位置 */ private int getValueEndEIndex(CharSequence msg, int valueEnd) { do { if (valueEnd == msg.length()) { break; } char c = msg.charAt(valueEnd); if (c == '"') { // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束 if (valueEnd + 1 == msg.length()) { break; } char next = msg.charAt(valueEnd + 1); if (next == ';' || next == ',' || next == '}' || next == ' ') { break; } else { valueEnd++; } } else if (c == ';' || c == ',' || c == '}') { break; } else { valueEnd++; } } while (true); return valueEnd; } }
日志脱敏
最新推荐文章于 2024-06-27 09:36:36 发布