<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ù)脫敏

          共 9544字,需瀏覽 20分鐘

           ·

          2022-07-30 15:58


          來源:juejin.cn/post/

          7110110794188062727

          • 思路
          • 代碼
            • 1. 自定義數(shù)據(jù)注解,并可以配置數(shù)據(jù)脫敏策略
            • 2. 自定義Serializer,參考jackson的StringSerializer,下面的示例只針對String類型進行脫敏
            • 3. 自定義AnnotationIntrospector,適配我們自定義注解返回相應的Serializer
            • 4. 覆蓋ObjectMapper
            • 5. 返回對象加上注解

          下午愜意時光,突然產(chǎn)品小姐姐走到我面前,打斷我短暫的摸魚time,企圖與我進行深入交流,還好我早有防備沒有閃,打開瑞star的點單頁面,暗示沒有一杯coffee解決不了的需求,需求是某些接口返回的信息,涉及到敏感數(shù)據(jù)的必須進行脫敏操作,我思考一反,表示某問題,馬上安排。

          思路

          1.要做成可配置多策略的脫敏操作,要不然一個個接口進行脫敏操作,重復的工作量太多,很顯然違背了“多寫一行算我輸”的程序員規(guī)范,思來想去,定義數(shù)據(jù)脫敏注解和數(shù)據(jù)脫敏邏輯的接口, 在返回類上,對需要進行脫敏的屬性加上,并指定對應的脫敏策略操作。

          2.接下來我只需要攔截控制器返回的數(shù)據(jù),找到帶有脫敏注解的屬性操作即可,一開始打算用@ControllerAdvice去實現(xiàn),但發(fā)現(xiàn)需要自己去反射類獲取注解,當返回對象比較復雜,需要遞歸去反射,性能一下子就會降低,于是換種思路,我想到平時使用的@JsonFormat,跟我現(xiàn)在的場景很類似,通過自定義注解跟字段解析器,對字段進行自定義解析,tql


          代碼

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

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

              DataMaskingFunc maskFunc() default DataMaskingFunc.NO_MASK;

          }

          2. 自定義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.classfalse);
                  this.operation = null;
              }

              public DataMaskingSerializer(DataMaskingOperation operation) {
                  super(String.classfalse);
                  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);
              }
          }

          3. 自定義AnnotationIntrospector,適配我們自定義注解返回相應的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;
              }

          }

          4. 覆蓋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;
                  }
              }

          }

          5. 返回對象加上注解

          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;

          }



          程序汪資料鏈接

          程序汪接的7個私活都在這里,經(jīng)驗整理

          Java項目分享  最新整理全集,找項目不累啦 07版

          堪稱神級的Spring Boot手冊,從基礎入門到實戰(zhàn)進階

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動總結(jié)的設計模式 PDF 火了,完整版開放下載!


          歡迎添加程序汪個人微信 itwang009  進粉絲群或圍觀朋友圈

          瀏覽 37
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  av在线资源 | 在线操比 | 欧美美女破处系列视频 | 日韩乱伦视频 | 午夜黄色直播 |