On top of @Skandya answer I had a requirement of adding a prefix and suffix to mask the data. here are the different implementations I did.
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
int prefix() default 0;
int suffix() default 0;
}
For the AnnotationIntrospector minor difference is to pass the prefix and suffix so that it can picked up by the Serializer.
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.NopAnnotationIntrospector;
public class SensitiveDataAnnotationIntrospector extends NopAnnotationIntrospector {
private static final long serialVersionUID = 1L;
@Override
public Object findSerializer(Annotated am) {
SensitiveData annotation = am.getAnnotation(SensitiveData.class);
if (annotation != null) {
return new SensitiveDataSerializer(annotation.prefix(), annotation.suffix());
}
return null;
}
@Override
public Object findDeserializer(Annotated am) {
SensitiveData annotation = am.getAnnotation(SensitiveData.class);
if (annotation != null) {
return SensitiveDataDeserializer.class;
}
return null;
}
}
This is my serializer class which is masking based on the prefix and suffix
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
public class SensitiveDataSerializer extends StdSerializer<String> {
private static final long serialVersionUID = 1L;
private int prefix = 0;
private int suffix = 0;
public SensitiveDataSerializer(int prefix, int suffix) {
super(String.class);
this.prefix = prefix;
this.suffix = suffix;
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// Masking data; for our example we are masking all characters'
gen.writeString(getFormattedString(value));
}
private String getFormattedString(String value) {
return value.substring(0, prefix)
.concat(value.substring(prefix, value.length() - suffix).replaceAll(".", "*"))
.concat(value.substring(value.length() - suffix));
}
}
The Tag and the object mapper would look like this
private class Bean {
public void setId(String id) {
this.id = id;
}
public void setEmailId(String emailId) {
this.emailId = emailId;
}
String id;
@SensitiveData(prefix = 1, suffix = 4)
String emailId;
}
And a Test would be
@Test
public void beanMappingTest() throws Exception {
Bean testBean = new Bean();
testBean.setId("1234");
testBean.setEmailId("test@test.com");
ObjectMapper om = new ObjectMapper();
AnnotationIntrospector is1 = AnnotationIntrospector.pair(
om.getSerializationConfig().getAnnotationIntrospector(), new SensitiveDataAnnotationIntrospector());
AnnotationIntrospector is2 = AnnotationIntrospector.pair(
om.getDeserializationConfig().getAnnotationIntrospector(), new SensitiveDataAnnotationIntrospector());
om.setAnnotationIntrospectors(is1, is2);
String expectedJson = "{\"id\":\"1234\",\"emailId\":\"t********.com\"}";
String maskedJson = om.writeValueAsString(testBean);
assertEquals(maskedJson, expectedJson);
}