阅读 349

SpringBoot 自定义注解之脱敏注解详解

这篇文章主要介绍了SpringBoot 自定义注解之脱敏注解详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

目录
  • 自定义注解之脱敏注解

    • 一、脱敏后的效果

    • 二、代码

      • 1.脱敏注解

      • 2.定义脱敏类型

      • 3.敏感工具类

      • 4.脱敏序列化信息

    • 小结一下

    • 自己手写的一个高效自定义字符串脱敏注解

      • 自己写了个 仅供参考

      自定义注解之脱敏注解

      数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。需求是把返回到前端的数据进行脱敏,以免造成隐私信息的泄露。

      一、脱敏后的效果

      未脱敏前

      这样显示很不好吧,所有信息都泄露了

      脱敏后

      这样就很好了吧

      二、代码

      1.脱敏注解

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.FIELD)
      @JacksonAnnotationsInside
      @JsonSerialize(using = SensitiveSerialize.class)
      public @interface Sensitive {
          /**
           * 脱敏数据类型
           */
          SensitiveTypeEnum type() default SensitiveTypeEnum.CUSTOMER;
          /**
           * 前置不需要打码的长度
           */
          int prefixNoMaskLen() default 0;
          /**
           * 后置不需要打码的长度
           */
          int suffixNoMaskLen() default 0;
          /**
           * 用什么打码
           */
          String symbol() default "*";
      }

      2.定义脱敏类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public enum SensitiveTypeEnum {
          /**
           * 自定义
           */
          CUSTOMER,
          /**
           * 姓名
           */
          NAME,
          /**
           * 身份证
           */
          ID_NUM,
          /**
           * 手机号码
           */
          PHONE_NUM
      }

      3.敏感工具类


      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
      public class DesensitizedUtils {
          /**
           * 对字符串进行脱敏操作
           *
           * @param origin          原始字符串
           * @param prefixNoMaskLen 左侧需要保留几位明文字段
           * @param suffixNoMaskLen 右侧需要保留几位明文字段
           * @param maskStr         用于遮罩的字符串, 如'*'
           * @return 脱敏后结果
           */
          public static String desValue(String origin, int prefixNoMaskLen, int suffixNoMaskLen, String maskStr) {
              if (origin == null) {
                  return null;
              }
              StringBuilder sb = new StringBuilder();
              for (int i = 0, n = origin.length(); i < n; i++) {
                  if (i < prefixNoMaskLen) {
                      sb.append(origin.charAt(i));
                      continue;
                  }
                  if (i > (n - suffixNoMaskLen - 1)) {
                      sb.append(origin.charAt(i));
                      continue;
                  }
                  sb.append(maskStr);
              }
              return sb.toString();
          }
          /**
           * 【中文姓名】只显示最后一个汉字,其他隐藏为星号,比如:**梦
           *
           * @param fullName 姓名
           * @return 结果
           */
          public static String chineseName(String fullName) {
              if (fullName == null) {
                  return null;
              }
              return desValue(fullName, 1, 0, "*");
          }
          /**
           * 【身份证号】显示前4位, 后2位,其他隐藏。
           *
           * @param id 身份证号码
           * @return 结果
           */
          public static String idCardNum(String id) {
              return desValue(id, 4, 2, "*");
          }
          /**
           * 【手机号码】前三位,后四位,其他隐藏。
           *
           * @param num 手机号码
           * @return 结果
           */
          public static String mobilePhone(String num) {
              return desValue(num, 3, 4, "*");
          }
      }

      4.脱敏序列化信息

      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
      @NoArgsConstructor
      @AllArgsConstructor
      public class SensitiveSerialize extends JsonSerializer<String> implements ContextualSerializer {
          /**
           * 脱敏类型
           */
          private SensitiveTypeEnum sensitiveTypeEnum;
          /**
           * 前几位不脱敏
           */
          private Integer prefixNoMaskLen;
          /**
           * 最后几位不脱敏
           */
          private Integer suffixNoMaskLen;
          /**
           * 用什么打码
           */
          private String symbol;
          @Override
          public void serialize(final String origin, final JsonGenerator jsonGenerator,
                                final SerializerProvider serializerProvider) throws IOException {
              switch (sensitiveTypeEnum) {
                  case CUSTOMER:
                      jsonGenerator.writeString(DesensitizedUtils.desValue(origin, prefixNoMaskLen, suffixNoMaskLen, symbol));
                      break;
                  case NAME:
                      jsonGenerator.writeString(DesensitizedUtils.chineseName(origin));
                      break;
                  case ID_NUM:
                      jsonGenerator.writeString(DesensitizedUtils.idCardNum(origin));
                      break;
                  case PHONE_NUM:
                      jsonGenerator.writeString(DesensitizedUtils.mobilePhone(origin));
                      break;
                  default:
                      throw new IllegalArgumentException("unknown sensitive type enum " + sensitiveTypeEnum);
              }
          }
          @Override
          public JsonSerializer<?> createContextual(final SerializerProvider serializerProvider,
                                                    final BeanProperty beanProperty) throws JsonMappingException {
              if (beanProperty != null) {
                  if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
                      Sensitive sensitive = beanProperty.getAnnotation(Sensitive.class);
                      if (sensitive == null) {
                          sensitive = beanProperty.getContextAnnotation(Sensitive.class);
                      }
                      if (sensitive != null) {
                          return new SensitiveSerialize(sensitive.type(), sensitive.prefixNoMaskLen(),
                                  sensitive.suffixNoMaskLen(), sensitive.symbol());
                      }
                  }
                  return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
              }
              return serializerProvider.findNullValueSerializer(null);
          }
      }

      小结一下

      该注解用于隐私数据的脱敏,只作用于类的属性上。该注解有四个属性,type表示脱敏数据类型(默认为CUSTOMER自定义,后面三个属性才有效),prefixNoMaskLen表示前置不需要打码的长度(默认为0),suffixNoMaskLen表示后置不需要打码的长度(默认为0),symbol表示用什么打码(默认为*)。

      一般用于返回对象给前端对象中包含隐私数据例如身份证、详细地址需要进行脱敏的情况。

      示例:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class UserInfo {
          @Sensitive(type = SensitiveTypeEnum.NAME)
          private String name;
          @Sensitive(type = SensitiveTypeEnum.ID_NUM)
          private String idNum;
          @Sensitive(type = SensitiveTypeEnum.PHONE_NUM)
          private String phone;
          @Sensitive(type = SensitiveTypeEnum.CUSTOMER, prefixNoMaskLen = 3, suffixNoMaskLen = 2, symbol = "#")
          private String address;
          @Sensitive(prefixNoMaskLen = 1, suffixNoMaskLen = 2, symbol = "*")
          private String password;
      }

      如果还有疑问我写了个demo,可以下载下来运行看看

      链接: 脱敏注解demo.

      自己手写的一个高效自定义字符串脱敏注解

      经理要求写一个自定义脱敏注解,百度查了一堆。都是效率比较低的

      自己写了个 仅供参考

      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
      /**
       * description: 数据脱敏
       * 1、默认不传部位、不传显示*号数量时字段全部脱敏
       *
       * 原始字符串 adminis 总长度从0计算 总数6
       * index=(0,2) size = 1 下标即从0到2以内的字符标注“ * ”,size=1 则只填充一个* size 不能超过截取字符
       * index=(2,3) size =2 下标即从2到3以内的字符标注“ * ”,size=2 则只填充二个* size 不能超过截取字符
       *
       * date: 2020/3/13 15:56
       *
       * @author oakdog
       * @version 1.0
       */
      @Target({ElementType.FIELD})
      @Retention(RetentionPolicy.RUNTIME)
      @JacksonAnnotationsInside
      @JsonSerialize(using = Desensitization.ConvertDesensitization.class)
      public @interface Desensitization {
          /**
           *  传入的下标索引
           *  规则 第一位起始下标 第二位是结束下标 默认值6位下标
           **/
          int[] index() default {0,6};
          /**
           *  需要脱敏的字符长度
           *  规则 输入 3 :则根据index下标索引对应脱敏3个字符 默认6个长度脱敏
           **/
          int size() default 6;
          class ConvertDesensitization extends StdSerializer<Object> implements ContextualSerializer {
              private int[] index;
              private int size;
              public ConvertDesensitization() {
                  super(Object.class);
              }
              private ConvertDesensitization(int[] index,int size) {
                  super(Object.class);
                  this.size = size;
                  this.index = index;
              }
              @Override
              public void serialize(Object value, JsonGenerator jgen,
                                    SerializerProvider provider) throws IOException {
                  char[] str = value.toString().toCharArray();
                  StringBuilder builder = new StringBuilder();
                  String char1 = (String) value;
                  if(str.length > 0) {
                      //字符长度超长处理
                      if(index[0] < str.length && index[1] < str.length) {
                          //使用默认初始值的脱敏处理
                          if(index[0] == 0) {
                              //如果输入脱敏大小长度小于0或大于原始脱敏字符长度,则全脱敏字符
                              if (size < 0 || size < str.length) {
                                  char[] charStr = char1.substring(index[1], str.length).toCharArray();
                                  char[] charStr1 = char1.substring(index[0], index[1]).toCharArray();
                                  builder.append(charStr1);
                                  for (int i = 0; i < charStr.length; i++) {
                                      if(size > i) {
                                          builder.append("*");
                                      }else {
                                          builder.append(charStr[i]);
                                      }
                                  }
                              }else {
                                  builder.append(getDefaultChar((String) value,"left"));
                              }
                          }else {
                              //从中间位置截取脱敏处理
                              //如果输入脱敏大小长度小于0或大于原始脱敏字符长度,则全脱敏字符
                              if (size < 0 || size < str.length) {
                                  char[] charStr = char1.substring(index[0], str.length - index[1] + 1).toCharArray(); //2 6-4 2 //中间截取部分
                                  List<Integer> prefix = getPrefix(index[0], (String) value);
                                  //List<Integer> suffix = getSuffix(index[0],index[1], (String) value);
                                  for (Integer integer : prefix) {
                                      builder.append(str[integer]);
                                  }
                                  for (int i = 0; i < charStr.length; i++) {
                                      if (size > i) {
                                          builder.append("*");
                                      } else {
                                          builder.append(charStr[i]);
                                      }
                                  }
                                  char[] chars = Arrays.copyOfRange(str, index[1], str.length);
                                  builder.append(String.valueOf(chars));
                              }else {
                                  builder.append(getDefaultChar((String) value,"right"));
                              }
                          }
                      }else {
                          //默认处理
                          builder.append(getDefaultChar((String) value,""));
                      }
                  }
                  jgen.writeString(builder.toString());
              }
              /**
               * 默认的填充方式
               * @param str 原始字符串
               * @param position 位置
               * @return
               */
              String getDefaultChar(String str,String position){
                  char[] desensitizationStr = str.toCharArray();
                  for(int i=0;i<desensitizationStr.length;i++){
                      if("left".equals(position)){
                          if(i != 0){
                              desensitizationStr[i] = '*';
                          }
                      }else if("right".equals(position)){
                          if(i != desensitizationStr.length-1){
                              desensitizationStr[i] = '*';
                          }
                      }else {
                          if(i != 0 && i != desensitizationStr.length-1){
                              desensitizationStr[i] = '*';
                          }
                      }
                  }
                  return String.valueOf(desensitizationStr);
              }
              /**
               * 获取字符前缀下标
               * @param index 下标
               * @param val 原始字符串
               * @return
               */
              List<Integer> getPrefix(int index,String val){
                  //int[] chars = {};
                  List<Integer> listIndex = new ArrayList<>();
                  for(int i=0;i<val.length();i++){
                      if(i != index){ //0 1 != 2
                          listIndex.add(i);
                          continue;
                      }
                      break;
                  }
                  return listIndex;
              }
              @Override
              public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
                  int[] index = {0,6}; //初始值
                  int size = 6; //初始值
                  Desensitization ann = null;
                  if (property != null) {
                      ann = property.getAnnotation(Desensitization.class);
                  }
                  if (ann != null) {
                      index = ann.index();
                      size = ann.size();
                  }
                  return new Desensitization.ConvertDesensitization(index,size);
              }
          }
      }

      以上为个人经验,希望能给大家一个参考

      原文链接:https://blog.csdn.net/a792396951/article/details/117993344


      文章分类
      代码人生
      版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
      相关推荐