<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          太強了,一個注解搞定接口返回數(shù)據(jù)脫敏

          共 13895字,需瀏覽 28分鐘

           ·

          2022-10-28 02:25

          ????關(guān)注后回復(fù) “進群” ,拉你進程序員交流群????


          目錄
          • 背景

          • 思路

          • 實現(xiàn)代碼


          背景


          下午愜意時光,突然產(chǎn)品小姐姐走到我面前,打斷我短暫的摸魚 time,企圖與我進行深入交流,還好我早有防備沒有閃,打開瑞 star 的點單頁面,暗示沒有一杯 coffee 解決不了的需求。


          需求是某些接口返回的信息,涉及到敏感數(shù)據(jù)的必須進行脫敏操作,我思考一反,表示某問題,馬上安排。


          思路


          ①要做成可配置多策略的脫敏操作,要不然一個個接口進行脫敏操作,重復(fù)的工作量太多,很顯然違背了“多寫一行算我輸”的程序員規(guī)范。


          思來想去,定義數(shù)據(jù)脫敏注解和數(shù)據(jù)脫敏邏輯的接口, 在返回類上,對需要進行脫敏的屬性加上,并指定對應(yīng)的脫敏策略操作。


          接下來我只需要攔截控制器返回的數(shù)據(jù),找到帶有脫敏注解的屬性操作即可,一開始打算用 @ControllerAdvice 去實現(xiàn),但發(fā)現(xiàn)需要自己去反射類獲取注解。


          當(dāng)返回對象比較復(fù)雜,需要遞歸去反射,性能一下子就會降低,于是換種思路,我想到平時使用的 @JsonFormat,跟我現(xiàn)在的場景很類似,通過自定義注解跟字段解析器,對字段進行自定義解析,tql。


          實現(xiàn)代碼


          自定義數(shù)據(jù)注解,并可以配置數(shù)據(jù)脫敏策略:

          @Target({ElementType.FIELDElementType.TYPE})
          @Retention(RetentionPolicy.RUNTIME)
          @Documented
          public @interface DataMasking {

              DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;

          }


          自定義 Serializer,參考 jackson 的 StringSerializer,下面的示例只針對 String 類型進行脫敏。


          public interface DataMaskingOperation {

              String MASK_CHAR = "*";

              String mask(String content, String maskChar);

          }


          public enum DataMaskingFunc {

               /**
               *  脫敏轉(zhuǎn)換器
               */

               NO_MASK((str, maskChar) -> {
                  return str;
               }),
               ALL_MASK((str, maskChar) -> {
                  if (StringUtils.hasLength(str)) {
                      StringBuilder sb = new StringBuilder();
                      for (int i = 0; i < str.length(); i++) {
                          sb.append(StringUtils.hasLength(maskChar) ? maskChar : DataMaskingOperation.MASK_CHAR);
                      }
                      return sb.toString();
                  } else {
                      return str;
                  }
              });

              private final DataMaskingOperation operation;

              private DataMaskingFunc(DataMaskingOperation operation) {
                  this.operation = operation;
              }

              public DataMaskingOperation operation() {
                  return this.operation;
              }

          }


          public final class DataMaskingSerializer extends StdScalarSerializer<Object{
              private final DataMaskingOperation operation;

              public DataMaskingSerializer() {
                  super(String.class, false);
                  this.operation = null;
              }

              public DataMaskingSerializer(DataMaskingOperation operation) {
                  super(String.class, false);
                  this.operation = operation;
              }


              public boolean isEmpty(SerializerProvider prov, Object value) {
                  String str = (String)value;
                  return str.isEmpty();
              }

              public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
                  if (Objects.isNull(operation)) {
                      String content = DataMaskingFunc.ALL_MASK.operation().mask((String) value, null);
                      gen.writeString(content);
                  } else {
                      String content = operation.mask((String) value, null);
                      gen.writeString(content);
                  }
              }

              public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider, TypeSerializer typeSer) throws IOException {
                  this.serialize(value, gen, provider);
              }

              public JsonNode getSchema(SerializerProvider provider, Type typeHint) {
                  return this.createSchemaNode("string"true);
              }

              public void acceptJsonFormatVisitor(JsonFormatVisitorWrapper visitor, JavaType typeHint) throws JsonMappingException {
                  this.visitStringFormat(visitor, typeHint);
              }
          }


          ③自定義 AnnotationIntrospector,適配我們自定義注解返回相應(yīng)的 Serializer。

          @Slf4j
          public class DataMaskingAnnotationIntrospector extends NopAnnotationIntrospector {

              @Override
              public Object findSerializer(Annotated am) {
                  DataMasking annotation = am.getAnnotation(DataMasking.class);
                  if (annotation != null) {
                      return new DataMaskingSerializer(annotation.maskFunc().operation());
                  }
                  return null;
              }

          }


          ④覆蓋 ObjectMapper:

          @Configuration(
                  proxyBeanMethods = false
          )
          public class DataMaskConfiguration {

              @Configuration(
                      proxyBeanMethods = false
              )
              @ConditionalOnClass({Jackson2ObjectMapperBuilder.class})
              static class JacksonObjectMapperConfiguration {
                  JacksonObjectMapperConfiguration() {
                  }

                  @Bean
                  @Primary
                  ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
                      ObjectMapper objectMapper = builder.createXmlMapper(false).build();
                      AnnotationIntrospector ai = objectMapper.getSerializationConfig().getAnnotationIntrospector();
                      AnnotationIntrospector newAi = AnnotationIntrospectorPair.pair(ai, new DataMaskingAnnotationIntrospector());
                      objectMapper.setAnnotationIntrospector(newAi);
                      return objectMapper;
                  }
              }

          }


          ⑤返回對象加上注解:

          public class User implements Serializable {
              /**
               * 主鍵ID
               */

              private Long id;

              /**
               * 姓名
               */

              @DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
              private String name;

              /**
               * 年齡
               */

              private Integer age;

              /**
               * 郵箱
               */

              @DataMasking(maskFunc = DataMaskingFunc.ALL_MASK)
              private String email;

          }
                                      
          來源:juejin.cn/post/7110110794188062727

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

          點擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點這里好文分享給更多人↓↓

          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲欧美色图区 | 成人网站在线观看mv视频 | 婷婷色色婷婷 | WwW69免费视频 | 亚洲第一在线观看视频 |