基于注解形式的数据脱敏
Posted bevis-byf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于注解形式的数据脱敏相关的知识,希望对你有一定的参考价值。
数据脱敏
注解定义
package cn.com.sensitive.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 敏感方法 * * @author zhanghao * @date 2019/06/04 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Sensitive { }
package cn.com.sensitive.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据脱敏 * 证件类型标识 * * @author zhanghao * @date 2019/06/04 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface SensitiveCardType { String idCardType() default "111"; String refField() default ""; }
package cn.com.sensitive.annotations; import cn.com.sensitive.enums.SensitiveType; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 数据脱敏 * 敏感字段 * * @author zhanghao * @date 2019/06/04 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) public @interface SensitiveField { SensitiveType value(); SensitiveCardType cardRef() default @SensitiveCardType(); }
枚举类定义
package cn.com.sensitive.enums; /** * 敏感数据类型 * * @author zhanghao * @date 2019/06/04 */ public enum SensitiveType { /** * 姓名 */ NAME, /** * 手机 */ MOBILE, /** * 邮箱 */ EMAIL, /** * 身份证 */ IDCARD, /** * 生日 */ BIRTHDAY, /** * 密码 */ PASSWORD, /** * 银行卡 */ BANKCARD, /** * 薪水 */ SALARY, /** * 其他证件号码 */ OTHERCARD, /** * 不确定哪种证件类型,指向某个字段确定类型 */ CARD_REF, /** * 对象(将对标记为OBJECT的对象会处理该对象里面的字段) */ OBJECT }
脱敏工具类定义
package cn.com.sensitive.utils; import cn.com.sensitive.SensitiveHandler; import org.apache.commons.lang3.StringUtils; import java.util.List; /** * 数据隐私显示 手机号,身份证号和银行卡号等 */ public class SensitiveUtils { /** * 手机号 * 手机号留前4位和后4位,其他* * * @param phone * @return */ public static String maskMobile(String phone) { if(StringUtils.isBlank(phone)) { return ""; } if(StringUtils.length(phone)<8) { return phone; } int start = 4; int end = phone.length() - 4; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(phone, overPlay.toString(), start, end); } /** * 邮箱账号 * 邮箱留前4位及@符号以后,其他* * * @param email * @return */ public static String maskEmail(String email) { if (StringUtils.isEmpty(email)) { return ""; } String at = "@"; if (!email.contains(at)) { return email; } /** * 这里主要逻辑是需要保留邮箱的注册商 比如@qq.com * 后四位打码,不足四位,除了@前都打码 */ int index = StringUtils.indexOf(email, at); String content = StringUtils.substring(email, 0, index); String mask = ""; if (content.length() > 4) { int start = 4; int end = index; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } mask = StringUtils.overlay(content, overPlay.toString(), 4, content.length()); } else { int start = 0; int end = index; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } mask = overPlay.toString(); } return mask + StringUtils.substring(email, index); } /** * 身份证打码操作 * 身份证留前4位和后3位,其他 * * * @param idCard * @return */ public static String maskIdCard(String idCard) { if(StringUtils.isBlank(idCard)) { return ""; } if(StringUtils.length(idCard)<7) { return idCard; } int start = 4; int end = idCard.length() - 3; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(idCard, overPlay.toString(), start, end); } /** * 生日打码操作 * 中间月日打码。生日年月日 * * @param birthday * @return */ public static String maskBirthday(String birthday) { if(StringUtils.isBlank(birthday)) { return ""; } if(StringUtils.length(birthday)<=4) { return birthday; } String pre = birthday.substring(0, 4); String suf = birthday.substring(4); String sufResult = StringUtils.replaceAll(suf, "[0-9]", "*"); return pre + sufResult; } /** * 银行卡号 * 银行账号留前4位和后4位,其他* * * @param bandCard * @return */ public static String maskBankCard(String bandCard) { if(StringUtils.isBlank(bandCard)) { return ""; } if(StringUtils.length(bandCard)<8) { return bandCard; } int start = 4; int end = bandCard.length() - 4; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(bandCard, overPlay.toString(), start, end); } /** * 密码全部打码 * * @param password * @return */ public static String maskPassword(String password) { if(StringUtils.isBlank(password)) { return ""; } int end = password.length(); StringBuffer overPlay = new StringBuffer(); for (int i = 0; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(password, overPlay.toString(), 0, end); } /** * 中文姓名,除了第一位不打码 * * @param name * @return */ public static String maskName(String name) { if(StringUtils.isBlank(name)) { return ""; } int end = name.length(); StringBuffer overPlay = new StringBuffer(); for (int i = 1; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(name, overPlay.toString(), 1, end); } /** * 月薪,全部* * * @param salary * @return */ public static String maskSalary(String salary) { if(StringUtils.isBlank(salary)) { return ""; } int end = salary.length(); StringBuffer overPlay = new StringBuffer(); for (int i = 0; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(salary, overPlay.toString(), 0, end); } /** * 其他证件号码前1位和后3位其他全部为* * * @param otherCard * @return */ public static String maskOtherCard(String otherCard) { if(StringUtils.isBlank(otherCard)) { return ""; } if(StringUtils.length(otherCard)<4) { return otherCard; } int start = 1; int end = otherCard.length() - 3; StringBuffer overPlay = new StringBuffer(); for (int i = start; i < end; i++) { overPlay.append("*"); } return StringUtils.overlay(otherCard, overPlay.toString(), start, end); } /** * 对list结果集支持 * @param list * @param <T> * @throws Exception */ public static <T> void supportList(List<T> list){ for (T t : list) { try { SensitiveHandler.handle(t); } catch (Exception e) { e.printStackTrace(); } } } /** * 对object结果集支持 * @param t * @param <T> * @throws Exception */ public static <T> void supportObject(T t){ try { SensitiveHandler.handle(t); } catch (Exception e) { e.printStackTrace(); } } }
核心处理器
package cn.com.sensitive; import cn.com.sensitive.annotations.SensitiveCardType; import cn.com.sensitive.annotations.SensitiveField; import cn.com.sensitive.enums.SensitiveType; import cn.com.sensitive.utils.SensitiveUtils; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.reflect.FieldUtils; import java.lang.reflect.Field; import java.util.List; import java.util.Objects; public class SensitiveHandler { public static void handle(Object obj) throws Exception { if (null != obj) { Class<?> objClazz = obj.getClass(); if (null != objClazz) { List<Field> allFieldsList = FieldUtils.getAllFieldsList(objClazz); if (CollectionUtils.isNotEmpty(allFieldsList)) { for (Field declaredField : allFieldsList) { declaredField.setAccessible(true); SensitiveField sensitiveField = declaredField.getAnnotation(SensitiveField.class); if (null != sensitiveField) { Object fieldVal = declaredField.get(obj); if (null != fieldVal) { if (SensitiveType.OBJECT.equals(sensitiveField.value())) { handle(fieldVal); } else if(SensitiveType.CARD_REF.equals(sensitiveField.value())) { // 处理 try { SensitiveCardType sensitiveCardType = sensitiveField.cardRef(); if(Objects.nonNull(sensitiveCardType)) { String idCardType = sensitiveCardType.idCardType(); String refField = sensitiveCardType.refField(); if(StringUtils.isNoneBlank(idCardType,refField)) { for (Field declaredFieldCur : allFieldsList) { declaredFieldCur.setAccessible(Boolean.TRUE); if(declaredFieldCur.getName().equals(refField)) { Object idCardTypeVal = declaredFieldCur.get(obj); String valStr = (String) fieldVal; if(String.valueOf(idCardTypeVal).equals(idCardType)) { String result = handleSensitiveString(SensitiveType.IDCARD, valStr); declaredField.set(obj, result); } else { String result = handleSensitiveString(SensitiveType.OTHERCARD, valStr); declaredField.set(obj, result); } break; } } } } } catch (Exception e) { e.printStackTrace(); } } else { // 处理 try { String valStr = (String) fieldVal; String result = handleSensitiveString(sensitiveField.value(), valStr); declaredField.set(obj, result); } catch (Exception e) { e.printStackTrace(); } } } } } } } } } public static String handleSensitiveString(SensitiveType type, String val) { try { String result = ""; switch (type) { case NAME: result = SensitiveUtils.maskName(val); break; case MOBILE: result = SensitiveUtils.maskMobile(val); break; case EMAIL: result = SensitiveUtils.maskEmail(val); break; case IDCARD: result = SensitiveUtils.maskIdCard(val); break; case BIRTHDAY: result = SensitiveUtils.maskBirthday(val); break; case PASSWORD: result = SensitiveUtils.maskPassword(val); break; case BANKCARD: result = SensitiveUtils.maskBankCard(val); break; case SALARY: result = SensitiveUtils.maskSalary(val); break; case OTHERCARD: result = SensitiveUtils.maskOtherCard(val); break; default: result = val; break; } return result; } catch (Exception e) { e.printStackTrace(); return val; } } }
WEB支持类
package cn.com.sensitive; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Order(9) @Component @Slf4j public class SensitiveAspect { @Pointcut("@annotation(cn.com.sensitive.annotations.Sensitive)") public void pointcut() { } @Around("pointcut()") public Object sensitiveAround(ProceedingJoinPoint pjp) throws Throwable { Object returnValue = pjp.proceed(); returnValue = SensitiveAspectSupport.sensitive(pjp, returnValue); return returnValue; } }
package cn.com.sensitive; import cn.com.LogUtils; import cn.com.sensitive.annotations.Sensitive; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.reflect.MethodSignature; import java.lang.reflect.Method; import java.util.Arrays; @Slf4j public class SensitiveAspectSupport { public static Object sensitive(JoinPoint pointer, Object returnValue) { Long methodStartTime = System.currentTimeMillis(); Signature signature = pointer.getSignature(); String methodDesc = signature.getDeclaringTypeName() + "." + signature.getName(); String msgDesc = "数据脱敏"; LogUtils.info(log, methodDesc, msgDesc, "arguments:{0} ", Arrays.toString(pointer.getArgs())); try { MethodSignature sign = (MethodSignature) signature; Method method = sign.getMethod(); //获取方法上的注解 Sensitive sensitive = method.getAnnotation(Sensitive.class); if(null != sensitive) { SensitiveHandler.handle(returnValue); } } catch (Exception e) { e.printStackTrace(); } Long methodEndTime = System.currentTimeMillis(); LogUtils.info(log, methodDesc, msgDesc, " 响应信息:{0}, 方法耗时:{1} 秒", "", (methodEndTime - methodStartTime) / 1000F); return returnValue; } }
以上是关于基于注解形式的数据脱敏的主要内容,如果未能解决你的问题,请参考以下文章
java 日志脱敏框架 sensitive,优雅的打印脱敏日志
java 日志脱敏框架 sensitive,优雅的打印脱敏日志