Description: 脱敏规则
17 | * @param plaintext 明文 18 | * @return java.lang.String 19 | * @author zhangjp 20 | * @date 2019/4/2 20:43 21 | */ 22 | String produceCipherText(String plaintext); 23 | 24 | void addDeviceToMap(); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhangjp/log4j2/desensitization/desensitization/DesensitizationUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhangjp.log4j2.desensitization.desensitization; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.util.DigestUtils; 5 | 6 | /** 7 | * 创建时间 2019年四月02日 星期二 17:00 8 | * 作者: zhangjp 9 | * 描述:脱敏工具类 10 | */ 11 | public class DesensitizationUtils extends StringUtils { 12 | 13 | /*** 14 | *Description: md5 方式
15 | * @param plaintext 明文 16 | * @return java.lang.String 17 | * @author zhangjp 18 | * @date 2019/4/2 17:10 19 | */ 20 | public static String md5(String plaintext) { 21 | return DigestUtils.md5DigestAsHex(plaintext.getBytes()); 22 | } 23 | 24 | /*** 25 | *Description: 手机号码前三后四脱敏
26 | * @param mobile 手机号 27 | * @return java.lang.String 28 | * @author zhangjp 29 | * @date 2019/4/2 17:11 30 | */ 31 | public static String mobileEncrypt(String mobile) { 32 | if (StringUtils.isEmpty(mobile) || (mobile.length() != 11)) { 33 | return mobile; 34 | } 35 | return mobile.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"); 36 | } 37 | 38 | /*** 39 | *Description: 身份证前三后四脱敏
40 | * 41 | * @param id 身份证号 42 | * @return java.lang.String 43 | * @author zhangjp 44 | * @date 2019/4/2 17:12 45 | */ 46 | public static String idEncrypt(String id) { 47 | if (StringUtils.isEmpty(id) || (id.length() < 8)) { 48 | return id; 49 | } 50 | return id.replaceAll("(?<=\\w{4})\\w(?=\\w{4})", "*"); 51 | } 52 | 53 | /*** 54 | *Description: 卡号三后四脱敏
55 | * @param card 卡号 56 | * @return java.lang.String 57 | * @author zhangjp 58 | * @date 2019/4/2 17:12 59 | */ 60 | public static String cardEncrypt(String card) { 61 | if (StringUtils.isEmpty(card) || (card.length() < 8)) { 62 | return card; 63 | } 64 | return card.replaceAll("(?<=\\w{4})\\w(?=\\w{4})", "*"); 65 | } 66 | 67 | /*** 68 | *Description: 姓名截取第一个字脱敏
69 | * @param name 名字 70 | * @return java.lang.String 71 | * @author zhangjp 72 | * @date 2019/4/2 17:12 73 | */ 74 | public static String nameEncrypt(String name) { 75 | if (StringUtils.isEmpty(name) || (name.length() < 2)) { 76 | return name; 77 | } 78 | return "*" + name.substring(1); 79 | } 80 | 81 | /** 82 | * [电子邮箱] 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示<例子:g**@163.com> 83 | * 84 | * @param email 邮箱 85 | * @return 脱敏后邮箱 86 | */ 87 | public static String emailEncrypt(String email) { 88 | if (StringUtils.isBlank(email)) { 89 | return ""; 90 | } 91 | int index = StringUtils.indexOf(email, "@"); 92 | if (index <= 3) 93 | return email; 94 | else 95 | return StringUtils.rightPad(StringUtils.left(email, 3), index, "*").concat(StringUtils.mid(email, index, StringUtils.length(email))); 96 | } 97 | 98 | /** 99 | * 判断subStr是否在str中 100 | * 12,13,a 是否包含数字1 false 101 | * 1,12,13,a 是否包含数字1 true 102 | * 103 | * @param str 字符串 104 | * @param subStr 需要判断的字符串 105 | * @param splitChar 分隔符 106 | */ 107 | public static boolean characterInString(String str, String subStr, char splitChar) { 108 | boolean result = false; 109 | int i = 0; 110 | while (i < str.length()) { 111 | // 分隔符直接跳过 112 | if (splitChar == str.charAt(i)) { 113 | i++; 114 | continue; 115 | } 116 | // 截取到下一分隔符直接的字符串 117 | int j = i + 1; 118 | StringBuilder sb = new StringBuilder().append(str.charAt(i)); 119 | while (j < str.length() && splitChar != str.charAt(j)) { 120 | sb.append(str.charAt(j)); 121 | j++; 122 | } 123 | 124 | // System.out.println(str); 125 | if (subStr.equals(sb.toString())) { 126 | result = true; 127 | break; 128 | } 129 | 130 | // 分隔符之后继续遍历 131 | i = j + 1; 132 | } 133 | return result; 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/main/java/com/zhangjp/log4j2/desensitization/desensitization/MD5Device.java: -------------------------------------------------------------------------------- 1 | package com.zhangjp.log4j2.desensitization.desensitization; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.util.DigestUtils; 5 | 6 | import javax.annotation.PostConstruct; 7 | 8 | /** 9 | * 创建时间 2019年四月04日 星期四 10:45 10 | * 作者: zhangjp 11 | * 描述:MD5方式脱敏器 12 | */ 13 | @Component 14 | public class MD5Device implements DesensitizationStrategy { 15 | @Override 16 | public String produceCipherText(String plaintext) { 17 | return DigestUtils.md5DigestAsHex(plaintext.getBytes()); 18 | } 19 | 20 | @PostConstruct 21 | @Override 22 | public void addDeviceToMap() { 23 | DEVICE_METHOD_MAP.put("md5", this); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/zhangjp/log4j2/desensitization/encry/AESUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhangjp.log4j2.desensitization.encry; 2 | 3 | import org.apache.commons.codec.binary.Hex; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import javax.crypto.Cipher; 9 | import javax.crypto.spec.SecretKeySpec; 10 | import java.io.UnsupportedEncodingException; 11 | 12 | /** 13 | * 创建时间 2019年七月18日 星期四 10:05 14 | * 描述:我家阳光818活动对接,Aes加密工具类 15 | */ 16 | public class AESUtil { 17 | private static final Logger LOG = LoggerFactory.getLogger(AESUtil.class); 18 | private static final String aesKey = "78dsfs54fs42afnlsale"; 19 | private static final String defaultCharset = "UTF-8"; 20 | 21 | 22 | 23 | /** 24 | * 使用aes加密 25 | * 功能和mysql的hex(AES_ENCRYPT(content,'key'))一样,使用utf-8编码 26 | * @param content 27 | * @param key 28 | * @return 29 | */ 30 | public static String AESEncrypt(String content, String key) { 31 | try { 32 | LOG.debug("加密前 <{}> ", content); 33 | if (StringUtils.isNotBlank(content)) { 34 | final Cipher encryptCipher = Cipher.getInstance("AES"); 35 | SecretKeySpec secretKeySpec = generateMySQLAESKey(key, defaultCharset); 36 | encryptCipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); 37 | byte valBytes[] = encryptCipher.doFinal(content.getBytes(defaultCharset)); 38 | String r = new String(Hex.encodeHex(valBytes)); 39 | LOG.debug("加密后 <{}> ", r); 40 | return r; 41 | } 42 | } catch (Exception e) { 43 | LOG.error("AESEncrypt加密失败:{}",e); 44 | } 45 | return content; 46 | } 47 | 48 | /** 49 | * 使用aes解密, 50 | * 功能和mysql的AES_DECRYPT(unhex(content),'key')一样,使用utf-8编码 51 | * @param content 52 | * @param key 53 | * @return 54 | */ 55 | public static String AESDecrypt(String content, String key) { 56 | if (StringUtils.isNotBlank(content)) { 57 | if (StringUtils.isNumeric(content.substring(0, content.length() - 1))) { 58 | return content; 59 | } else { 60 | try { 61 | LOG.debug("解密前 <{}> ", content); 62 | Cipher decryptCipher = Cipher.getInstance("AES"); 63 | SecretKeySpec secretKeySpec = generateMySQLAESKey(key, "UTF-8"); 64 | decryptCipher.init(2, secretKeySpec); 65 | byte[] valBytes = decryptCipher.doFinal(Hex.decodeHex(content.toCharArray())); 66 | String r = new String(valBytes); 67 | LOG.debug("解密后 <{}> ", r); 68 | return r; 69 | } catch (Exception e) { 70 | LOG.debug("解密失败后 <{}> ", content); 71 | return content; 72 | } 73 | } 74 | } else { 75 | return content; 76 | } 77 | } 78 | 79 | /** 80 | * 使用aes加密,使用默认key 81 | * 功能和mysql的hex(AES_ENCRYPT(content,'key'))一样,使用utf-8编码 82 | * @param content 83 | * @return 84 | */ 85 | public static String AESEncrypt(String content) { 86 | return AESEncrypt(content, aesKey); 87 | } 88 | 89 | /** 90 | * 使用aes解密,使用默认key。 91 | * 功能和mysql的AES_DECRYPT(unhex(content),'key')一样,使用utf-8编码 92 | * @param content 93 | * @return 94 | * @throws Exception 95 | */ 96 | public static String AESDecrypt(String content) { 97 | return AESDecrypt(content, aesKey); 98 | } 99 | 100 | /** 101 | * @param key 102 | * @param encoding 103 | * @return 104 | */ 105 | public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) { 106 | try { 107 | final byte[] finalKey = new byte[16]; 108 | int i = 0; 109 | for (byte b : key.getBytes(encoding)) { 110 | finalKey[i++ % 16] ^= b; 111 | } 112 | return new SecretKeySpec(finalKey, "AES"); 113 | } catch (UnsupportedEncodingException e) { 114 | throw new RuntimeException(e); 115 | } 116 | } 117 | 118 | 119 | 120 | public static void main(String[] args) { 121 | String name = ""; 122 | String phone = "13513210010"; 123 | String certificate = ""; 124 | String aesname = AESEncrypt(name); 125 | String aesphone = AESEncrypt(phone); 126 | String aescertificate = AESEncrypt(certificate); 127 | System.out.println("-----------------AES解密------------------------"); 128 | AESDecrypt(name); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/com/zhangjp/log4j2/desensitization/encry/ArgumentResolver.java: -------------------------------------------------------------------------------- 1 | package com.zhangjp.log4j2.desensitization.encry; 2 | 3 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; 4 | import org.springframework.core.MethodParameter; 5 | import org.springframework.web.bind.support.WebDataBinderFactory; 6 | import org.springframework.web.context.request.NativeWebRequest; 7 | import org.springframework.web.method.support.HandlerMethodArgumentResolver; 8 | import org.springframework.web.method.support.ModelAndViewContainer; 9 | 10 | /** 11 | * 创建时间 2019年九月12日 星期四 17:10 12 | * 作者: zhangjp 13 | * 描述:TODO 14 | */ 15 | public class ArgumentResolver implements HandlerMethodArgumentResolver { 16 | 17 | public ArgumentResolver() {} 18 | 19 | @Override 20 | public boolean supportsParameter(MethodParameter parameter) { 21 | return parameter.hasParameterAnnotation(JsonSerialize.class); 22 | } 23 | 24 | @Override 25 | public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { 26 | System.out.println("parameter = " + parameter); 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/zhangjp/log4j2/desensitization/encry/JacksonEncry.java: -------------------------------------------------------------------------------- 1 | package com.zhangjp.log4j2.desensitization.encry; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.core.JsonProcessingException; 5 | import com.fasterxml.jackson.databind.JsonSerializer; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | import org.apache.commons.lang3.StringUtils; 8 | 9 | import java.io.IOException; 10 | 11 | /** 12 | * 创建时间 2019年九月12日 星期四 15:44 13 | * 作者: zhangjp 14 | */ 15 | public class JacksonEncry extends JsonSerializer
130 | * Implementations are free to return a new StringBuilder if they can
131 | * detect in advance that the specified StringBuilder is too small.
132 | */
133 | private StringBuilder toText(final Serializer2 serializer, final LogEvent event,
134 | final StringBuilder destination) {
135 | return serializer.toSerializable(event, destination);
136 | }
137 |
138 | /**
139 | * Creates a PatternParser.
140 | *
141 | * @param config The Configuration.
142 | * @return The PatternParser.
143 | */
144 | public static PatternParser createPatternParser(final Configuration config) {
145 | if (config == null) {
146 | return new PatternParser(config, KEY, LogEventPatternConverter.class);
147 | }
148 | PatternParser parser = config.getComponent(KEY);
149 | if (parser == null) {
150 | parser = new PatternParser(config, KEY, LogEventPatternConverter.class);
151 | config.addComponent(KEY, parser);
152 | parser = config.getComponent(KEY);
153 | }
154 | return parser;
155 | }
156 |
157 | @Override
158 | public String toString() {
159 | return patternSelector == null ? conversionPattern : patternSelector.toString();
160 | }
161 |
162 | /**
163 | * Creates a pattern layout.
164 | *
165 | * @param pattern The pattern. If not specified, defaults to DEFAULT_CONVERSION_PATTERN.
166 | * @param patternSelector Allows different patterns to be used based on some selection criteria.
167 | * @param config The Configuration. Some Converters require access to the Interpolator.
168 | * @param replace A Regex replacement String.
169 | * @param charset The character set. The platform default is used if not specified.
170 | * @param alwaysWriteExceptions If {@code "true"} (default) exceptions are always written even if the pattern contains no exception tokens.
171 | * @param noConsoleNoAnsi If {@code "true"} (default is false) and {@link System#console()} is null, do not output ANSI escape codes
172 | * @param headerPattern The footer to place at the top of the document, once.
173 | * @param footerPattern The footer to place at the bottom of the document, once.
174 | * @return The PatternLayout.
175 | */
176 | @PluginFactory
177 | public static MyPatternLayout createLayout(
178 | @PluginAttribute(value = "pattern", defaultString = DEFAULT_CONVERSION_PATTERN) final String pattern,
179 | @PluginElement("PatternSelector") final PatternSelector patternSelector,
180 | @PluginConfiguration final Configuration config,
181 | @PluginElement("jsonReplaces") final JsonRegexReplaces replace,
182 | // LOG4J2-783 use platform default by default, so do not specify defaultString for charset
183 | @PluginAttribute(value = "charset") final Charset charset,
184 | @PluginAttribute(value = "alwaysWriteExceptions", defaultBoolean = true) final boolean alwaysWriteExceptions,
185 | @PluginAttribute(value = "noConsoleNoAnsi", defaultBoolean = false) final boolean noConsoleNoAnsi,
186 | @PluginAttribute("header") final String headerPattern,
187 | @PluginAttribute("footer") final String footerPattern) {
188 | return newBuilder()
189 | .withPattern(pattern)
190 | .withPatternSelector(patternSelector)
191 | .withConfiguration(config)
192 | .withRegexReplacement(replace)
193 | .withCharset(charset)
194 | .withAlwaysWriteExceptions(alwaysWriteExceptions)
195 | .withNoConsoleNoAnsi(noConsoleNoAnsi)
196 | .withHeader(headerPattern)
197 | .withFooter(footerPattern)
198 | .build();
199 | }
200 |
201 | private static class PatternSerializer implements Serializer, Serializer2 {
202 |
203 | private final PatternFormatter[] formatters;
204 | private final JsonRegexReplaces replace;
205 |
206 | private PatternSerializer(final PatternFormatter[] formatters, final JsonRegexReplaces replace) {
207 | super();
208 | this.formatters = formatters;
209 | this.replace = replace;
210 | }
211 |
212 | @Override
213 | public String toSerializable(final LogEvent event) {
214 | final StringBuilder sb = getStringBuilder();
215 | try {
216 | return toSerializable(event, sb).toString();
217 | } finally {
218 | trimToMaxSize(sb);
219 | }
220 | }
221 |
222 | @Override
223 | public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
224 | final int len = formatters.length;
225 | for (int i = 0; i < len; i++) {
226 | formatters[i].format(event, buffer);
227 | }
228 | if (replace != null) { // creates temporary objects
229 | String str = buffer.toString();
230 | str = replace.format(str);
231 | buffer.setLength(0);
232 | buffer.append(str);
233 | }
234 | return buffer;
235 | }
236 |
237 | @Override
238 | public String toString() {
239 | final StringBuilder builder = new StringBuilder();
240 | builder.append(super.toString());
241 | builder.append("[formatters=");
242 | builder.append(Arrays.toString(formatters));
243 | builder.append(", replace=");
244 | builder.append(replace);
245 | builder.append("]");
246 | return builder.toString();
247 | }
248 | }
249 |
250 | private static class PatternSelectorSerializer implements Serializer, Serializer2 {
251 |
252 | private final PatternSelector patternSelector;
253 | private final JsonRegexReplaces replace;
254 |
255 | private PatternSelectorSerializer(final PatternSelector patternSelector, final JsonRegexReplaces replace) {
256 | super();
257 | this.patternSelector = patternSelector;
258 | this.replace = replace;
259 | }
260 |
261 | @Override
262 | public String toSerializable(final LogEvent event) {
263 | final StringBuilder sb = getStringBuilder();
264 | try {
265 | return toSerializable(event, sb).toString();
266 | } finally {
267 | trimToMaxSize(sb);
268 | }
269 | }
270 |
271 | @Override
272 | public StringBuilder toSerializable(final LogEvent event, final StringBuilder buffer) {
273 | final PatternFormatter[] formatters = patternSelector.getFormatters(event);
274 | final int len = formatters.length;
275 | for (int i = 0; i < len; i++) {
276 | formatters[i].format(event, buffer);
277 | }
278 | if (replace != null) { // creates temporary objects
279 | String str = buffer.toString();
280 | str = replace.format(str);
281 | buffer.setLength(0);
282 | buffer.append(str);
283 | }
284 | return buffer;
285 | }
286 |
287 | @Override
288 | public String toString() {
289 | return super.toString() +
290 | "[patternSelector=" +
291 | patternSelector +
292 | ", replaces=" +
293 | replace +
294 | "]";
295 | }
296 | }
297 |
298 | /**
299 | * Creates a PatternLayout using the default options. These options include using UTF-8, the default conversion
300 | * pattern, exceptions being written, and with ANSI escape codes.
301 | *
302 | * @return the PatternLayout.
303 | * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern
304 | */
305 | public static MyPatternLayout createDefaultLayout() {
306 | return newBuilder().build();
307 | }
308 |
309 | /**
310 | * Creates a PatternLayout using the default options and the given configuration. These options include using UTF-8,
311 | * the default conversion pattern, exceptions being written, and with ANSI escape codes.
312 | *
313 | * @param configuration The Configuration.
314 | * @return the PatternLayout.
315 | * @see #DEFAULT_CONVERSION_PATTERN Default conversion pattern
316 | */
317 | public static MyPatternLayout createDefaultLayout(final Configuration configuration) {
318 | return newBuilder().withConfiguration(configuration).build();
319 | }
320 |
321 | /**
322 | * Creates a builder for a custom PatternLayout.
323 | *
324 | * @return a PatternLayout builder.
325 | */
326 | @PluginBuilderFactory
327 | public static MyPatternLayout.Builder newBuilder() {
328 | return new MyPatternLayout.Builder();
329 | }
330 |
331 | /**
332 | * Custom PatternLayout builder. Use the {@link PatternLayout#newBuilder() builder factory method} to create this.
333 | */
334 | public static class Builder implements org.apache.logging.log4j.core.util.Builder