SpringBoot中對(duì)于字符串進(jìn)行脫敏操作

問(wèn)題

在電商業(yè)務(wù)中彪置,對(duì)于一些敏感數(shù)據(jù)(比如 用戶姓名,用戶身份證蝇恶,用戶手機(jī)號(hào)碼拳魁,用戶銀行卡等),需要進(jìn)行脫敏操作撮弧,為了業(yè)務(wù)開發(fā)的方便潘懊,應(yīng)該要提供對(duì)應(yīng)的處理方法, 能夠使得業(yè)務(wù)開發(fā)能夠自由的配置贿衍。

方案

所以采取了和之前文章中授舟,我們采取的方案是,重新修改默認(rèn)的ObjectMapper對(duì)象贸辈,添加一個(gè)自定義類型的DesensitizationBeanSerializerModifier释树,通過(guò)對(duì)于所有的String字符串都進(jìn)行過(guò)濾,并且提供了 對(duì)應(yīng)的注解 Desensitization擎淤,和一些參數(shù)奢啥, 可以根據(jù)不同的脫敏規(guī)則進(jìn)行自由的配置

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

  //前后分別展示多少個(gè)字符
  int showPrefixLength() default 3;
  int showSuffixLength() default 4;

  //針對(duì)長(zhǎng)度進(jìn)行多種處理,0為不考慮長(zhǎng)度
  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);
    }

  }

具體的針對(duì)脫敏注解的邏輯操作

/**
  * 處理字符串脫敏處理
  */
 @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;
   }
 }

這樣對(duì)于一個(gè)Bean里面的對(duì)象嘴拢,都可以進(jìn)行相關(guān)操作扫尺。

下面給一個(gè)例子


@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);
  }

執(zhí)行的結(jié)果是

{
  "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"
      }
    ]
  }
}

這樣業(yè)務(wù)放就只要簡(jiǎn)單的在想要脫敏的字段上加入對(duì)應(yīng)的注解和配置對(duì)應(yīng)的參數(shù)就可以了。

結(jié)束

轉(zhuǎn)載請(qǐng)注明作者和出處炊汤,并添加本頁(yè)鏈接正驻。
原文鏈接: //tech.cnhnb.com/post/6

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市抢腐,隨后出現(xiàn)的幾起案子姑曙,更是在濱河造成了極大的恐慌,老刑警劉巖迈倍,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件伤靠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宴合,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門焕梅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人卦洽,你說(shuō)我怎么就攤上這事贞言。” “怎么了阀蒂?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵该窗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蚤霞,道長(zhǎng)酗失,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任昧绣,我火速辦了婚禮规肴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘夜畴。我一直安慰自己拖刃,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布斩启。 她就那樣靜靜地躺著,像睡著了一般醉锅。 火紅的嫁衣襯著肌膚如雪兔簇。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天硬耍,我揣著相機(jī)與錄音垄琐,去河邊找鬼。 笑死经柴,一個(gè)胖子當(dāng)著我的面吹牛狸窘,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播坯认,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼翻擒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了牛哺?” 一聲冷哼從身側(cè)響起陋气,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎引润,沒(méi)想到半個(gè)月后巩趁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淳附,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年议慰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蠢古。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡别凹,死狀恐怖草讶,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情番川,我是刑警寧澤到涂,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站颁督,受9級(jí)特大地震影響践啄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜沉御,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一屿讽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吠裆,春花似錦伐谈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至祝旷,卻和暖如春履澳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怀跛。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工距贷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吻谋。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓忠蝗,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親漓拾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阁最,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359