本文概览:在logback层面实现对日志中敏感信息进行过滤。
1 背景
1、 背景
就是需要对日志中敏感信息进行遮盖。之前考虑的是在代码使用logger.info时查看相应的参数是否进行了遮盖,但是统计了使用logger的类有2000+个,这样修改起来比较麻烦。所以就考虑在logback层面进行实现。
2、重点
(1)如何在Logback的层面实现?通过自定义PatternLayout来实现。
(2)定义邮件、手机号、银行卡号和身份证信息的正则。
2 实现
包括定义PaternLayout和在logback.xml配置两部分来实现。
2.1 定义PatternLayout
1、实现原理
- 对于获取到日志信息,进行正则匹配,获取到匹配的字符串matchStr
- 对匹配到字符串进行遮盖处理,得到maskStr
- 将处理后的字符串maskStr替换查找到字符串matchStr。
2、具体代码实现如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
/** * 对敏感信息进行掩盖。 * 1.实现原理 * 对产生的日志信息,进行正则匹配和替换。 * <p> * 2.目前包括如下类型的信息:银行卡号、电话、身份证和邮箱。 * <p> * 3.如何进行扩展新的正则类型 * (1)在PatternType枚举中新增一个正则 * (2)extractMatchesByType对新增的正则做处理 * (3)maskByType对新增的正则做处理 * <p> */ public class MaskingPatternLayout extends PatternLayout { /** * 匹配的所有正则 */ private Map<PatternType, Pattern> patternsMap = Maps.newHashMap(); public MaskingPatternLayout() { loadPatterns(); } @Override public String doLayout(ILoggingEvent event) { String message = super.doLayout(event); if (CollectionUtil.isEmpty(patternsMap)) { return message; } // 处理日志信息 try { return process(message); } catch (Exception e) { // 这里不做任何操作,直接返回原来message return message; } } /** * 加载正则表达式,生成相应的Pattern对象。 */ private void loadPatterns() { for (PatternType patternType : PatternType.values()) { Pattern pattern = Pattern.compile(patternType.getRegex()); patternsMap.put(patternType, pattern); } } /** * 替换信息 * * @param message * @return */ public String process(String message) { for (PatternType key : patternsMap.keySet()) { // 1.生成matcher Pattern pattern = patternsMap.get(key); Matcher matcher = pattern.matcher(message); // 2.获取匹配的信息 Set<String> matches = extractMatchesByType(matcher); // 3.掩盖匹配的信息 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 = Sets.newHashSet(); 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 */ private String maskByType(PatternType key, String message, Set<String> matchs) { if (key == PatternType.BANK_CARD | key == PatternType.ID_CARD | key == PatternType.PHONE_NUMBER) { // 处理数字的信息 return maskNumber(message, matchs); } else if (key == PatternType.EMAIL) { // 处理email的信息 return maskEmail(message, matchs); } else { return message; } } /** * 掩盖数字类型信息 * * @param message * @param matches * @return */ private String maskNumber(String message, Set<String> matches) { for (String match : matches) { // 1.处理获取的字符 String matchProcess = InfoMaskUtil.mask(match); // 2.String的替换 message = message.replace(match, matchProcess); } return message; } /** * 掩盖email的信息 * * @param message * @param matches * @return */ private String maskEmail(String message, Set<String> matches) { for (String match : matches) { // 1.处理获取的字符 String matchProcess = InfoMaskUtil.maskEmail(match); // 2.String的替换 message = message.replace(match, matchProcess); } return message; } /** * 定义敏感信息类型 */ private enum PatternType { // 1.手机号共11位,模式为: 13xxx,,14xxx,15xxx,17xxx,18xx PHONE_NUMBER("手机号", "[^\\d](1[34578]\\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}"), // 4. 15位(全为数字位)或者18位身份证(17位位数字位,最后一位位校验位) ID_CARD("身份证", "[^\\d](\\d{15})[^\\d]|[^\\d](\\d{18})[^\\d]|[^\\d](\\d{17}X)"); 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; } } } |
2.2 在logback中进行配置
之前logback.xml中配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<appender name="DEFAULT" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/default.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_HOME}/%d{yyyyMMdd}/default.log</fileNamePattern> <maxHistory>30</maxHistory> <cleanHistoryOnStart>true</cleanHistoryOnStart> </rollingPolicy> <encoder> <pattern>${DEFAULT_PATTERN}</pattern> <charset>${CHARSET}</charset> </encoder> </appender> |
对<encoder>替换如下:
1 2 3 4 5 6 |
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"> <layout class="com.mytest.MaskingPatternLayout"> <pattern>${DEFAULT_PATTERN}</pattern> </layout> <charset>${CHARSET}</charset> </encoder> |
2.3 总结
1、 定义邮件、手机号、银行卡号和身份证信息的正则。
(1)银行卡号
1 |
[^\d](\d{16})[^\d]|[^\d](\d{19})[^\d] |
(2)手机号
1 |
[^\d](1[34578]\d{9})[^\d] |
(3)邮箱
1 |
[A-Za-z_0-9]{1,64}@[A-Za-z1-9_-]+.[A-Za-z]{2,10} |
(4)身份证信息
1 |
[^\d](\d{15})[^\d]|[^\d](\d{18})[^\d]|[^\d](\d{17}X) |
更多正则表达式可以参考
3 代码优化
还可以使用java SPI来重构代码,每一种类型正则都对应一个处理器。具体代码后续在github上给出。
关于java SPI参考
4 参考
1、http://stackoverflow.com/questions/25277930/mask-sensitive-data-in-logs-with-logback
(全文完)