簡體   English   中英

使用傑克遜掩蓋json領域

[英]Mask json fields using jackson

我正在嘗試使用jackson進行序列化時屏蔽敏感數據。

我嘗試過使用@JsonSerialize和自定義注釋@Mask。

Mask.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Mask {
  String value() default "XXX-DEFAULT MASK FORMAT-XXX";
}

Employee.java

import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.util.Map;

public class Employee {

  @Mask(value = "*** The value of this attribute is masked for security reason ***")
  @JsonSerialize(using = MaskStringValueSerializer.class)
  protected String name;

  @Mask
  @JsonSerialize(using = MaskStringValueSerializer.class)
  protected String empId;

  @JsonSerialize(using = MaskMapStringValueSerializer.class)
  protected Map<Category, String> categoryMap;

  public Employee() {
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getEmpId() {
    return empId;
  }

  public void setEmpId(String empId) {
    this.empId = empId;
  }

  public Map<Category, String> getCategoryMap() {
    return categoryMap;
  }

  public void setCategoryMap(Map<Category, String> categoryMap) {
    this.categoryMap = categoryMap;
  }
}

Category.java

public enum Category {
  @Mask
  CATEGORY1,
  @Mask(value = "*** This value of this attribute is masked for security reason ***")
  CATEGORY2,
  CATEGORY3;
}

MaskMapStringValueSerializer.java

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.util.Map;

public class MaskMapStringValueSerializer extends JsonSerializer<Map<Category, String>> {

  @Override
  public void serialize(Map<Category, String> map, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    jsonGenerator.writeStartObject();

    for (Category key : map.keySet()) {
      Mask annot = null;
      try {
        annot = key.getClass().getField(key.name()).getAnnotation(Mask.class);
      } catch (NoSuchFieldException e) {
        e.printStackTrace();
      }

      if (annot != null) {
        jsonGenerator.writeStringField(((Category) key).name(), annot.value());
      } else {
        jsonGenerator.writeObjectField(((Category) key).name(), map.get(key));
      }
    }

    jsonGenerator.writeEndObject();

  }
}

MaskStringValueSerializer.java

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class MaskStringValueSerializer extends StdSerializer<String> implements ContextualSerializer {
  private Mask annot;

  public MaskStringValueSerializer() {
    super(String.class);
  }

  public MaskStringValueSerializer(Mask logMaskAnnotation) {
    super(String.class);
    this.annot = logMaskAnnotation;
  }

  public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    if (annot != null && s != null && !s.isEmpty()) {
      jsonGenerator.writeString(annot.value());
    } else {
      jsonGenerator.writeString(s);
    }
  }

  public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
    Mask annot = null;
    if (beanProperty != null) {
      annot = beanProperty.getAnnotation(Mask.class);
    }
    return new MaskStringValueSerializer(annot);

  }
}

MaskValueTest.java

import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class MaskValueTest {


  public static void main(String args[]) throws Exception{
    Employee employee = new Employee();

    employee.setName("John Doe");
    employee.setEmpId("1234567890");
    Map<Category, String> catMap = new HashMap<>();
    catMap.put(Category.CATEGORY1, "CATEGORY1");
    catMap.put(Category.CATEGORY2, "CATEGORY2");
    catMap.put(Category.CATEGORY3, "CATEGORY3");
    employee.setCategoryMap(catMap);

    ObjectMapper mapper = new ObjectMapper();
    System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(employee));
  }
}

輸出 -

{
  "name" : "*** The value of this attribute is masked for security reason ***",
  "empId" : "XXX-DEFAULT MASK FORMAT-XXX",
  "categoryMap" : {
    "CATEGORY1" : "XXX-DEFAULT MASK FORMAT-XXX",
    "CATEGORY2" : "*** The value of this attribute is masked for security reason ***",
    "CATEGORY3" : "CATEGORY3"
  }
}
  • 結果是按照預期,然而,這似乎是靜態掩蔽。
  • 目的是僅在需要時屏蔽,例如在日志中打印時應該屏蔽所有這些敏感數據。
  • 如果我必須發送這個json用於文檔索引,其值應該是原樣,這個實現失敗了。

我正在尋找一個基於注釋的解決方案,我可以使用JsonSerializers初始化的2個不同的ObjectMapper實例。

您可以創建模塊來捆綁序列化程序並使用objectmapper注冊模塊,而不是使用MaskStringValueSerializer.java,這最終將允許您擁有兩個不同的objectmapper實例。

創建一個模塊以捆綁序列化程序

public class MaskingModule extends SimpleModule {
    private static final String NAME = "CustomIntervalModule";
    private static final VersionUtil VERSION_UTIL = new VersionUtil() {};

    public MaskingModule() {
      super(NAME, VERSION_UTIL.version());
      addSerializer(MyBean.class, new MaskMapStringValueSerializer());
    }
}

使用ObjectMapper注冊模塊並使用它

 ObjectMapper objectMapper = new ObjectMapper().registerModule(new MaskingModule());
 System.out.println(objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(employee));

您還可以擴展Object Mapper,注冊模塊並使用它

public class CustomObjectMapper extends ObjectMapper {
    public CustomObjectMapper() {
      registerModule(new MaskingModule());
    }
  }


 CustomObjectMapper customObjectMapper = new CustomObjectMapper ();
 System.out.println(customObjectMapper .writerWithDefaultPrettyPrinter().writeValueAsString(employee));

取出@JsonSerialize注釋,並把如何處理的邏輯@Mask在注釋Module ,例如,有它添加AnnotationIntrospector

您現在可以選擇是否調用registerModule(Module module)

至於編寫模塊,我會把它留給你。 如果您對此有任何疑問,請提出另一個問題。

為什么不使用兩個參數作為原始值,一個參數作為屏蔽值。 例如,在這種情況下,您可以使用String name和String maskedName。 然后,對於日志記錄,您可以使用屏蔽值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM