我有一个要求,我已经创建了一个自定义注释@MaskSensitiveData。我注释敏感字段。像
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。 我需要自定义Object Mapper以屏蔽cardNumber字段以返回json 。 请建议一个更好的方法。
答案 0 :(得分:4)
以下是使用自定义JsonSerializer解决问题的方法。 从blog post开始遵循步骤。
创建自定义序列化程序
<div class="arrow"></div>
创建一个模块以捆绑序列化程序
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();
}
}
使用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 MaskingSerializer());
}
}
测试代码
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
registerModule(new MaskingModule());
}
}
答案 1 :(得分:2)
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 :(得分:0)
我使用的是由Spring boot配置的 GENERAL&SHARED ObjectMapper
,并且我不希望它被setSerializerFactory()
污染或重写整个{{1 }}。这是我的解决方案:
BeanSerializer
@Configuration
@AutoConfigureAfter(JacksonAutoConfiguration.class)
public static class ExtJacksonConfig {
@Autowired
private ObjectMapper objectMapper;
@PostConstruct
public void postConstruct() throws JsonMappingException {
SimpleModule module = new SimpleModule();
module.addSerializer(ProductOrder.class, new POProductOrderSerializer(
(BeanSerializerBase) objectMapper.getSerializerFactory().createSerializer(
objectMapper.getSerializerProviderInstance(),
objectMapper.getSerializationConfig().constructType(ProductOrder.class))));
objectMapper.registerModule(module);
}
}
public class SensitiveDataSerializer<T> extends BeanSerializer {
private final Function<T, Boolean> authorityChecker;
private final String maskText;
public SensitiveDataSerializer(BeanSerializerBase src, Function<T, Boolean> authorityChecker,
String maskText) {
super(src);
this.authorityChecker = authorityChecker;
this.maskText = Optional.ofNullable(maskText).orElse("****");
assert(this.authorityChecker != null);
assert(!Checker.isEmpty(sensitiveFieldNames));
// Replace BeanPropertyWriter
for (int i=0; i<_props.length; i++) {
if (_props[i] != null && _props[i].getAnnotation(MaskSensitiveData.class) != null) {
_props[i] = new SensitivePropertyWriter(_props[i]);
}
}
for (int j=0; j<_filteredProps.length; j++) {
if (_filteredProps[j] != null && _filteredProps[j].getAnnotation(MaskSensitiveData.class) != null) {
_filteredProps[j] = new SensitivePropertyWriter(_filteredProps[j]);
}
}
}
class SensitivePropertyWriter extends BeanPropertyWriter {
private final BeanPropertyWriter writer;
SensitivePropertyWriter(BeanPropertyWriter writer) {
super(writer);
this.writer = writer;
}
@Override
public void serializeAsField(Object bean,
JsonGenerator gen,
SerializerProvider prov) throws Exception {
if (authorityChecker.apply((T) bean)) {
super.serializeAsField(bean, gen, prov);
return;
}
gen.writeStringField(writer.getName(), maskText);
}
}
}
答案 3 :(得分:0)
在@Skandya 回答之上,我需要添加前缀和后缀来屏蔽数据。这是我所做的不同实现。
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
int prefix() default 0;
int suffix() default 0;
}
对于 AnnotationIntrospector 的细微差别是传递前缀和后缀,以便序列化程序可以获取它。
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;
}
}
这是我的序列化器类,它基于前缀和后缀进行屏蔽
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));
}
}
标签和对象映射器看起来像这样
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;
}
测试将是
@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);
}