2016-01-23 153 views
6

我有一個要求,我已經創建了一個自定義註釋@MaskSensitiveData。我註釋敏感領域。像自定義Jackson ObjectMapper讀取自定義註釋和掩碼字段註釋

class MyBean { 
    String userName; 
    @MaskSensitiveData 
    String cardNumber; 
    String abc; 
    String xyz; 
} 

ObjectMapper mapper = new ObjectMapper(); 
    String json = null; 
    AnnotationIntrospector primary = new JaxbAnnotationIntrospector(); 
    AnnotationIntrospector secondary = new JacksonAnnotationIntrospector(); 
    AnnotationIntrospector pair = new AnnotationIntrospectorPair(primary, secondary); 
    mapper.setAnnotationIntrospector(pair); 
    try { 
     json = mapper.writeValueAsString(obj); 
     /* 
     * if(json != null) { json = getLoggableString(json); } 
     */ 
    } catch (Exception e) { 
     return "Unable to convert to Json object:" + obj.toString() + " Message: " + e.getMessage(); 

    } 

我使用的是Jackson ObjectMapper將objct轉換爲Json。 我需要自定義對象映射器來屏蔽cardNumber字段以返回json。 請建議一個更好的方法。

回答

0
package stackoverflow; 

import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.is; 

import java.io.IOException; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 

import org.hamcrest.Matchers; 
import org.junit.Test; 

import com.fasterxml.jackson.core.JsonGenerator; 
import com.fasterxml.jackson.core.JsonParser; 
import com.fasterxml.jackson.core.JsonProcessingException; 
import com.fasterxml.jackson.databind.AnnotationIntrospector; 
import com.fasterxml.jackson.databind.DeserializationContext; 
import com.fasterxml.jackson.databind.ObjectMapper; 
import com.fasterxml.jackson.databind.SerializerProvider; 
import com.fasterxml.jackson.databind.deser.std.StdDeserializer; 
import com.fasterxml.jackson.databind.introspect.Annotated; 
import com.fasterxml.jackson.databind.introspect.AnnotationIntrospectorPair; 
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector; 
import com.fasterxml.jackson.databind.ser.std.StdSerializer; 

public class MaskingAnnotationExample { 
    // Define @custom Annotation 
    // assumed to be used by String type field for this example 
    @Retention(RetentionPolicy.RUNTIME) 
    static @interface MaskSensitiveData { 
    } 

    public static class MyBean { 
     private String userName; 

     @MaskSensitiveData 
     private String cardNumber; 

     public MyBean() { 
     } 

     public String getCardNumber() { 
      return cardNumber; 
     } 

     public String getUserName() { 
      return userName; 
     } 

     public void setUserName(String userName) { 
      this.userName = userName; 
     } 

     public void setCardNumber(String cardNumber) { 
      this.cardNumber = cardNumber; 
     } 
    } 

    // map the Serializer/Deserializer based on custom annotation 
    public static class MaskSensitiveDataAnnotationIntrospector extends NopAnnotationIntrospector { 
     private static final long serialVersionUID = 1L; 

     @Override 
     public Object findSerializer(Annotated am) { 
      MaskSensitiveData annotation = am.getAnnotation(MaskSensitiveData.class); 
      if (annotation != null) { 
       return MaskSensitiveDataSerializer.class; 
      } 

      return null; 
     } 

     @Override 
     public Object findDeserializer(Annotated am) { 
      MaskSensitiveData annotation = am.getAnnotation(MaskSensitiveData.class); 
      if (annotation != null) { 
       return MaskSensitiveDataDeserializer.class; 
      } 

      return null; 
     } 
    } 

    public static class MaskSensitiveDataDeserializer extends StdDeserializer<String> { 
     private static final long serialVersionUID = 1L; 

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

     @Override 
     public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { 
      // un-masking logic here. in our example we are removing "MASK" 
      // string 
      String s = p.getValueAsString(); 
      return s.substring(4); 
     } 
    } 

    public static class MaskSensitiveDataSerializer extends StdSerializer<String> { 
     private static final long serialVersionUID = 1L; 

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

     @Override 
     public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException { 
      // Masking data; for our example we are adding 'MASK' 
      gen.writeString("MASK" + value); 
     } 
    } 

    @Test 
    public void demo() throws Exception { 
     ObjectMapper mapper = new ObjectMapper(); 

     AnnotationIntrospector sis = mapper.getSerializationConfig().getAnnotationIntrospector(); 
     AnnotationIntrospector dis = mapper.getDeserializationConfig().getAnnotationIntrospector(); 

     AnnotationIntrospector is1 = AnnotationIntrospectorPair.pair(sis, new MaskSensitiveDataAnnotationIntrospector()); 
     AnnotationIntrospector is2 = AnnotationIntrospectorPair.pair(dis, new MaskSensitiveDataAnnotationIntrospector()); 

     mapper.setAnnotationIntrospectors(is1, is2); 

     MyBean obj = new MyBean(); 
     obj.setUserName("Saurabh Bhardwaj"); 
     obj.setCardNumber("4455-7788-9999-7777"); 
     String json = mapper.writeValueAsString(obj); 

     String expectedJson = "{\"userName\":\"Saurabh Bhardwaj\",\"cardNumber\":\"MASK4455-7788-9999-7777\"}"; 
     assertThat(json, Matchers.is(expectedJson)); 

     MyBean cloned = mapper.readValue(json, MyBean.class); 
     assertThat(cloned.getCardNumber(), is(obj.getCardNumber())); 
    } 
} 

希望這會有所幫助。

2

下面是使用自定義JsonSerializer的問題的解決方案。 從此blog post的步驟。

創建一個自定義序列

public class MaskingSerializer extends JsonSerializer <MyBean> { 

    @ 
    Override 
    public void serialize(MyBean value, JsonGenerator jGen, SerializerProvider serializers) throws IOException, JsonProcessingException { 
    jGen.writeStartObject(); 

    Field[] fields = value.getClass().getDeclaredFields(); 
    for (Field field: fields) { 
     field.setAccessible(true); 
     MaskSensitiveData mask = field.getDeclaredAnnotation(MaskSensitiveData.class); 

     try { 
     if (mask != null) { 
      field.setAccessible(true); 
      field.set(value, field.get(value).toString().replaceAll(".", "*")); 
     } 

     jGen.writeStringField(field.getName(), field.get(value).toString()); 


     } catch (IllegalArgumentException e) { 
     e.printStackTrace(); 
     } catch (IllegalAccessException e) { 
     e.printStackTrace(); 
     } 
    } 

    jGen.writeEndObject(); 

    } 

} 

創建一個模塊捆綁串行

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 MaskingSerializer()); 
    } 
} 

註冊與ObjectMapper模塊。

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

測試代碼

public class MyBeanTest { 

    private static final CustomObjectMapper OBJECT_MAPPER = 
      new CustomObjectMapper(); 
    @Test 
    public void testIntervalSerialization() throws Exception { 
     MyBean mb = new MyBean(); 
     mb.setAbc("value"); 
     mb.setCardNumber("4441114443335551"); 
     mb.setUserName("User"); 
     mb.setXyz("value"); 
     String result = OBJECT_MAPPER.writeValueAsString(mb); 
     System.out.println(result); 
     String expected = "{\"userName\":\"User\",\"cardNumber\":\"****************\",\"abc\":\"value\",\"xyz\":\"value\"}"; 
     Assert.assertEquals(expected, result); 
    } 
} 
+0

這是罰款,只要所有的屬性都是String類型。如果您將abc作爲具有自己屬性的更復雜的類型,那麼您將如何將它們打印爲json?我也有這個問題,不知道解決方案呢... – dune76

+0

https://stackoverflow.com/q/46021769/5667890這是我目前的問題,我仍然沒有線索 – dune76