1.自定义脱敏注解

标注在实体类 具体 需要脱敏 字段上
其中用到Jackson的两个注解, 并标明使用我们自定义脱敏策略即序列化后脱敏

  1. import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
  2. import com.fasterxml.jackson.databind.annotation.JsonSerialize;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. /**
  8. * 对象脱敏注解
  9. *
  10. * @version v1.0
  11. **/
  12. @Retention(RetentionPolicy.RUNTIME)
  13. @Target(ElementType.FIELD)
  14. @JacksonAnnotationsInside
  15. @JsonSerialize(using = SensitiveSerialize.class)
  16. public @interface Sensitive {
  17. /**
  18. * 脱敏数据类型, 非Customer时, 将忽略 refixNoMaskLen 和 suffixNoMaskLen 和 maskStr
  19. */
  20. SensitiveTypeEnum type() default SensitiveTypeEnum.CUSTOMER;
  21. /**
  22. * 前置不需要打码的长度
  23. */
  24. int prefixNoMaskLen() default 0;
  25. /**
  26. * 后置不需要打码的长度
  27. */
  28. int suffixNoMaskLen() default 0;
  29. /**
  30. * 用什么打码
  31. */
  32. String maskStr() default "*";
  33. }

注解按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;

2.自定义脱敏逻辑

借助Jackson类和接口实现序列化才脱敏

  1. import com.fasterxml.jackson.core.JsonGenerator;
  2. import com.fasterxml.jackson.databind.BeanProperty;
  3. import com.fasterxml.jackson.databind.JsonMappingException;
  4. import com.fasterxml.jackson.databind.JsonSerializer;
  5. import com.fasterxml.jackson.databind.SerializerProvider;
  6. import com.fasterxml.jackson.databind.ser.ContextualSerializer;
  7. import com.pofly.air.common.core.util.DesensitizedUtils;
  8. import lombok.AllArgsConstructor;
  9. import lombok.NoArgsConstructor;
  10. import java.io.IOException;
  11. import java.util.Objects;
  12. /**
  13. * <p>
  14. * 脱敏序列化
  15. */
  16. @NoArgsConstructor
  17. @AllArgsConstructor
  18. public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {
  19. private SensitiveTypeEnum type;
  20. private Integer prefixNoMaskLen;
  21. private Integer suffixNoMaskLen;
  22. private String maskStr;
  23. @Override
  24. public void serialize(final String origin, final JsonGenerator jsonGenerator,
  25. final SerializerProvider serializerProvider) throws IOException {
  26. switch (type) {
  27. case CHINESE_NAME:
  28. jsonGenerator.writeString(DesensitizedUtils.chineseName(origin));
  29. break;
  30. case ID_CARD:
  31. jsonGenerator.writeString(DesensitizedUtils.idCardNum(origin));
  32. break;
  33. case FIXED_PHONE:
  34. jsonGenerator.writeString(DesensitizedUtils.fixedPhone(origin));
  35. break;
  36. case MOBILE_PHONE:
  37. jsonGenerator.writeString(DesensitizedUtils.mobilePhone(origin));
  38. break;
  39. case ADDRESS:
  40. jsonGenerator.writeString(DesensitizedUtils.address(origin));
  41. break;
  42. case EMAIL:
  43. jsonGenerator.writeString(DesensitizedUtils.email(origin));
  44. break;
  45. case BANK_CARD:
  46. jsonGenerator.writeString(DesensitizedUtils.bankCard(origin));
  47. break;
  48. case PASSWORD:
  49. jsonGenerator.writeString(DesensitizedUtils.password(origin));
  50. break;
  51. case KEY:
  52. jsonGenerator.writeString(DesensitizedUtils.key(origin));
  53. break;
  54. case CUSTOMER:
  55. jsonGenerator.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, maskStr));
  56. break;
  57. default:
  58. throw new IllegalArgumentException("Unknow sensitive type enum " + type);
  59. }
  60. }
  61. @Override
  62. public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
  63. final BeanProperty beanProperty) throws JsonMappingException {
  64. if (beanProperty != null) {
  65. if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
  66. Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
  67. if (sensitive == null) {
  68. sensitive = beanProperty.getContextAnnotation(Sensitive.class);
  69. }
  70. if (sensitive != null) {
  71. return new SensitiveSerialize(sensitive.type(), sensitive.prefixNoMaskLen(),
  72. sensitive.suffixNoMaskLen(), sensitive.maskStr());
  73. }
  74. }
  75. return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
  76. }
  77. return serializerProvider.findNullValueSerializer(null);
  78. }
  79. }

3.脱敏枚举类

  1. /**
  2. * 敏感信息枚举类
  3. *
  4. * @version v1.0
  5. **/
  6. public enum SensitiveTypeEnum {
  7. /**
  8. * 自定义
  9. */
  10. CUSTOMER,
  11. /**
  12. * 用户名, 刘*华, 徐*
  13. */
  14. CHINESE_NAME,
  15. /**
  16. * 身份证号, 110110********1234
  17. */
  18. ID_CARD,
  19. /**
  20. * 座机号, ****1234
  21. */
  22. FIXED_PHONE,
  23. /**
  24. * 手机号, 176****1234
  25. */
  26. MOBILE_PHONE,
  27. /**
  28. * 地址, 北京********
  29. */
  30. ADDRESS,
  31. /**
  32. * 电子邮件, s*****o@xx.com
  33. */
  34. EMAIL,
  35. /**
  36. * 银行卡, 622202************1234
  37. */
  38. BANK_CARD,
  39. /**
  40. * 密码, 永远是 ******, 与长度无关
  41. */
  42. PASSWORD,
  43. /**
  44. * 密钥, 【密钥】密钥除了最后三位其他都是***, 与长度无关
  45. */
  46. KEY
  47. }

4.脱敏工具类

  1. import cn.hutool.core.util.StrUtil;
  2. /**
  3. * 脱敏工具类
  4. *
  5. * @version v1.0
  6. **/
  7. public class DesensitizedUtils {
  8. /**
  9. * 对字符串进行脱敏操作
  10. * @param origin 原始字符串
  11. * @param prefixNoMaskLen 左侧需要保留几位明文字段
  12. * @param suffixNoMaskLen 右侧需要保留几位明文字段
  13. * @param maskStr 用于遮罩的字符串, 如'*'
  14. * @return 脱敏后结果
  15. */
  16. public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
  17. if (origin == null) {
  18. return null;
  19. }
  20. StringBuilder sb = new StringBuilder();
  21. for (int i = 0, n = origin.length(); i < n; i++) {
  22. if (i < prefixNoMaskLen) {
  23. sb.append(origin.charAt(i));
  24. continue;
  25. }
  26. if (i > (n - suffixNoMaskLen - 1)) {
  27. sb.append(origin.charAt(i));
  28. continue;
  29. }
  30. sb.append(maskStr);
  31. }
  32. return sb.toString();
  33. }
  34. /**
  35. * 【中文姓名】只显示最后一个汉字,其他隐藏为星号,比如:**梦
  36. * @param fullName 姓名
  37. * @return 结果
  38. */
  39. public static String chineseName(String fullName) {
  40. if (fullName == null) {
  41. return null;
  42. }
  43. return desValue(fullName, 0, 1, "*");
  44. }
  45. /**
  46. * 【身份证号】显示前六位, 四位,其他隐藏。共计18位或者15位,比如:340304*******1234
  47. * @param id 身份证号码
  48. * @return 结果
  49. */
  50. public static String idCardNum(String id) {
  51. return desValue(id, 6, 4, "*");
  52. }
  53. /**
  54. * 【固定电话】后四位,其他隐藏,比如 ****1234
  55. * @param num 固定电话
  56. * @return 结果
  57. */
  58. public static String fixedPhone(String num) {
  59. return desValue(num, 0, 4, "*");
  60. }
  61. /**
  62. * 【手机号码】前三位,后四位,其他隐藏,比如135****6810
  63. * @param num 手机号码
  64. * @return 结果
  65. */
  66. public static String mobilePhone(String num) {
  67. return desValue(num, 3, 4, "*");
  68. }
  69. /**
  70. * 【地址】只显示到地区,不显示详细地址,比如:北京市海淀区****
  71. * @param address 地址
  72. * @return 结果
  73. */
  74. public static String address(String address) {
  75. return desValue(address, 6, 0, "*");
  76. }
  77. /**
  78. * 【电子邮箱 邮箱前缀仅显示第一个字母,前缀其他隐藏,用星号代替,@及后面的地址显示,比如:d**@126.com
  79. * @param email 电子邮箱
  80. * @return 结果
  81. */
  82. public static String email(String email) {
  83. if (email == null) {
  84. return null;
  85. }
  86. int index = StrUtil.indexOf(email, '@');
  87. if (index <= 1) {
  88. return email;
  89. }
  90. String preEmail = desValue(email.substring(0, index), 1, 0, "*");
  91. return preEmail + email.substring(index);
  92. }
  93. /**
  94. * 【银行卡号】前六位,后四位,其他用星号隐藏每位1个星号,比如:622260**********1234
  95. * @param cardNum 银行卡号
  96. * @return 结果
  97. */
  98. public static String bankCard(String cardNum) {
  99. return desValue(cardNum, 6, 4, "*");
  100. }
  101. /**
  102. * 【密码】密码的全部字符都用*代替,比如:******
  103. * @param password 密码
  104. * @return 结果
  105. */
  106. public static String password(String password) {
  107. if (password == null) {
  108. return null;
  109. }
  110. return "******";
  111. }
  112. /**
  113. * 【密钥】密钥除了最后三位,全部都用*代替,比如:***xdS 脱敏后长度为6,如果明文长度不足三位,则按实际长度显示,剩余位置补*
  114. * @param key 密钥
  115. * @return 结果
  116. */
  117. public static String key(String key) {
  118. if (key == null) {
  119. return null;
  120. }
  121. int viewLength = 6;
  122. StringBuilder tmpKey = new StringBuilder(desValue(key, 0, 3, "*"));
  123. if (tmpKey.length() > viewLength) {
  124. return tmpKey.substring(tmpKey.length() - viewLength);
  125. }
  126. else if (tmpKey.length() < viewLength) {
  127. int buffLength = viewLength - tmpKey.length();
  128. for (int i = 0; i < buffLength; i++) {
  129. tmpKey.insert(0, "*");
  130. }
  131. return tmpKey.toString();
  132. }
  133. else {
  134. return tmpKey.toString();
  135. }
  136. }
  137. }