SpringBoot中对于字符串进行脱敏操作

问题

在电商业务中,对于一些敏感数据(比如 用户姓名,用户身份证,用户手机号码,用户银行卡等),需要进行脱敏操作,为了业务开发的方便,应该要提供对应的处理方法, 能够使得业务开发能够自由的配置。

方案

所以采取了和之前文章中,我们采取的方案是,重新修改默认的ObjectMapper对象,添加一个自定义类型的DesensitizationBeanSerializerModifier,通过对于所有的String字符串都进行过滤,并且提供了 对应的注解 Desensitization,和一些参数, 可以根据不同的脱敏规则进行自由的配置

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@Documented
public @interface Desensitization {

  //前后分别展示多少个字符
  int showPrefixLength() default 3;
  int showSuffixLength() default 4;

  //针对长度进行多种处理,0为不考虑长度
  int strLength() default 0;

}

下面为自己定义的序列化方法

public static class DesensitizationBeanSerializerModifier extends BeanSerializerModifier {

    @Override
    public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
        BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
      for (Object beanProperty : beanProperties) {
        BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
        if (isStringType(writer)) {
          Desensitization desensitization = ((BeanPropertyWriter) beanProperty).getAnnotation(Desensitization.class);
          Desensitizations desensitizations = ((BeanPropertyWriter) beanProperty).getAnnotation(Desensitizations.class);
          if (desensitization != null || desensitizations != null) {
            StringDesensitizationJsonSerializer stringDesensitizationJsonSerializer = new StringDesensitizationJsonSerializer();
            if (desensitization != null) {
              stringDesensitizationJsonSerializer.setDesensitization(desensitization);
            }
            if (desensitizations != null) {
              stringDesensitizationJsonSerializer.setDesensitizations(desensitizations);
            }

            writer.assignSerializer(stringDesensitizationJsonSerializer);
          }
        }
      }
      return beanProperties;
    }

    /**
     * 是否是string
     */
    private boolean isStringType(BeanPropertyWriter writer) {
      Class<?> clazz = writer.getType().getRawClass();
      return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
    }

  }

具体的针对脱敏注解的逻辑操作

/**
  * 处理字符串脱敏处理
  */
 @NoArgsConstructor
 @AllArgsConstructor
 public static class StringDesensitizationJsonSerializer extends JsonSerializer<Object> {

   @Setter
   private Desensitization desensitization;
   @Setter
   private Desensitizations desensitizations;

   @Override
   public void serialize(Object o, JsonGenerator jsonGenerator,
       SerializerProvider serializerProvider) throws IOException {
     if (desensitization == null && desensitizations == null) {
       return;
     }
     Desensitization[] desensitizationArray = getDesensitizationArray();
     String value = o.toString();

     int valueLength = value.length();
     boolean matchSize = false;
     int zeroLength = -1;
     int index = 0;
     for (Desensitization desensitization : desensitizationArray) {
       if (valueLength == desensitization.strLength()) {
         matchSize = true;
         jsonGenerator
             .writeString(fixedVal(value, desensitization.showPrefixLength(), desensitization.showSuffixLength()));
       } else if (desensitization.strLength() == 0 && zeroLength == -1) {
         zeroLength = index++;
       } else {
         index++;
       }
     }

     if (!matchSize) {
       if(zeroLength != -1) {
         jsonGenerator
             .writeString(fixedVal(value, desensitizationArray[zeroLength].showPrefixLength(),
                 desensitizationArray[zeroLength].showSuffixLength()));
       } else {
         jsonGenerator
             .writeString(fixedVal(value, desensitizationArray[0].showPrefixLength(),
                 desensitizationArray[0].showSuffixLength()));
       }
     }

   }

   private Desensitization[] getDesensitizationArray() {
     int size = 0;
     if (desensitization != null) {
       size++;
     }

     if (desensitizations != null) {
       size += desensitizations.value().length;
     }

     Desensitization[] desensitizationArray = new Desensitization[size];
     int index = 0;
     if (desensitization != null) {
       desensitizationArray[index++] = desensitization;
     }

     if (desensitizations != null) {
       System.arraycopy(desensitizations.value(), 0, desensitizationArray, index, size - index);
     }
     return desensitizationArray;
   }

   private String fixedVal(String value, int prefixLength, int suffixLength) {
     if (StringUtils.isBlank(value)) {
       return Strings.repeat("*", value.length());
     }

     if(value.length() <= prefixLength + suffixLength) {
       return value;
     }
     int length = value.length();
     String prefix = value.substring(0, prefixLength);
     String suffix = value.substring(length - suffixLength);
     String stars = Strings.repeat("*", length - (prefixLength + suffixLength));
     return prefix + stars + suffix;
   }
 }

这样对于一个Bean里面的对象,都可以进行相关操作。

下面给一个例子


@Data
public class DesensitizationsBean {

  @Desensitizations(value = {
      @Desensitization(strLength = 4, showPrefixLength = 2, showSuffixLength = 1),
      @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
  })
  private String mobileA;
  @Desensitizations(value = {
      @Desensitization,
      @Desensitization(strLength = 1, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
  })
  private String mobileB;
  @Desensitizations(value = {
      @Desensitization,
      @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
  })
  private String mobileC;
  @Desensitizations(value = {
      @Desensitization,
      @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
  })
  private String mobileD;
  @Desensitizations(value = {
      @Desensitization,
      @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
  })
  private String mobileE;
  @Desensitizations(value = {
      @Desensitization,
      @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
      @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
      @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
      @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
  })
  private String mobileF;
  private List<DesensitizationsSubBean> subBeanList;

  @Data
  @Builder
  @NoArgsConstructor
  @AllArgsConstructor
  public static class DesensitizationsSubBean {

    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private Long mobile1;
    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private String mobile2;
    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private String mobile3;
    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private String mobile4;
    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private String mobile5;
    @Desensitizations(value = {
        @Desensitization,
        @Desensitization(strLength = 1, showPrefixLength = 1, showSuffixLength = 1),
        @Desensitization(strLength = 2, showPrefixLength = 1, showSuffixLength = 0),
        @Desensitization(strLength = 3, showPrefixLength = 0, showSuffixLength = 1),
        @Desensitization(strLength = 4, showPrefixLength = 1, showSuffixLength = 1),
    })
    private String mobile6;
  }
}


@GetMapping("/desensitizations")
  public BaseResult desensitizations() {
    DesensitizationsBean desensitizationsBean = new DesensitizationsBean();
    desensitizationsBean.setMobileA("18507313226");
    desensitizationsBean.setMobileB("龙");
    desensitizationsBean.setMobileC("龙哲");
    desensitizationsBean.setMobileD("龙若妍");
    desensitizationsBean.setMobileE("龙若妍好");
    desensitizationsBean.setMobileF("18507313226");

    desensitizationsBean.setSubBeanList(
        Lists.newArrayList(
            DesensitizationsSubBean.builder().mobile1(18507313226L).mobile2("龙").mobile3("龙哲").mobile4("龙若妍").mobile5("龙若妍好").mobile6("18507313226").build(),
            DesensitizationsSubBean.builder().mobile1(18507313226L).mobile2("龙").mobile3("龙哲").mobile4("龙若妍").mobile5("龙若妍好").mobile6("18507313226").build(),
            DesensitizationsSubBean.builder().mobile1(18507313226L).mobile2("龙").mobile3("龙哲").mobile4("龙若妍").mobile5("龙若妍好").mobile6("18507313226").build()
        ));

    return BaseResult.success(desensitizationsBean);
  }

执行的结果是

{
  "code": 0,
  "msg": "success",
  "traceId": "6ba0c884b10fbd8f",
  "data": {
    "mobileA": "18********6",
    "mobileB": "龙",
    "mobileC": "龙*",
    "mobileD": "**妍",
    "mobileE": "龙**好",
    "mobileF": "185****3226",
    "subBeanList": [
      {
        "mobile1": 18507313226,
        "mobile2": "龙",
        "mobile3": "龙*",
        "mobile4": "**妍",
        "mobile5": "龙**好",
        "mobile6": "185****3226"
      },
      {
        "mobile1": 18507313226,
        "mobile2": "龙",
        "mobile3": "龙*",
        "mobile4": "**妍",
        "mobile5": "龙**好",
        "mobile6": "185****3226"
      },
      {
        "mobile1": 18507313226,
        "mobile2": "龙",
        "mobile3": "龙*",
        "mobile4": "**妍",
        "mobile5": "龙**好",
        "mobile6": "185****3226"
      }
    ]
  }
}

这样业务放就只要简单的在想要脱敏的字段上加入对应的注解和配置对应的参数就可以了。

结束

转载请注明作者和出处,并添加本页链接。
原文链接: //tech.cnhnb.com/post/6

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345