Jackson是Java平台非常流行的JSON解析框架,而且扩展性很强,本文通过添加一个Module来加强JSON字段的解析来实现单个字段的加密与解密功能。
基本想法是使用Jackson来处理自定义的注解,在需要加密/解密的字段上添加上相应的注解,先定义个自定义的注解:
@JacksonAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Encrypt {}
然后在需要在POJO中对字段添加@Encrypt注解:
public final class User {
@Encrypt
private String name;
private String guid;
@Encrypt
private User first;
@Encrypt
private int age;
//... getters/setters
}
完成上面的工作后,然后再来扩展Jackson来处理@Encrypt注解进行字段加密,先定义个Jackson的Module:
public class EncryptionModule extends Module {
public final static String ARTIFACT_ID = "jackson-hb-encryption";
public final static Version VERSION = new Version(1, 0, 0, null);
private EncryptionModule(){
}
...
@Override
public void setupModule(SetupContext setupContext) {
setupContext.addBeanSerializerModifier(new EncryptedSerializerModifier());
setupContext.addBeanDeserializerModifier(new EncryptedDeserializerModifier());
}
/**
* 创建一个{@link ObjectMapper}对象,支持{@link Encrypt}注解。
* @return
*/
public static ObjectMapper createMapper(){
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonSerialize.Inclusion.NON_NULL);
objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure( SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.registerModule(new EncryptionModule());
return objectMapper;
}
}
创建module只需要继承org.codehaus.jackson.map.Module类,EncryptionModule在setupModule方法中添加了一个序列化修改器和反序列化修改器,EncryptedSerializerModifier重载changeProperties方法,遍历属性如果属性有@Encrypt注解那么需要将属性的JsonSerializer设置为EncryptedJsonSerializer。
public class EncryptedSerializerModifier extends BeanSerializerModifier {
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BasicBeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
/*
遍历beanProperties处理Encrypt.class注解
*/
List<BeanPropertyWriter> newWriter = new ArrayList<>();
for(BeanPropertyWriter writer : beanProperties){
if(null == writer.getAnnotation(Encrypt.class)){
newWriter.add(writer);
}else{
JsonSerializer<Object> serializer = new EncryptedJsonSerializer(writer.getSerializer());
newWriter.add(writer.withSerializer(serializer));
}
}
return newWriter;
}
}
EncryptedJsonSerializer是自定义的序列化工具,它实现属性字段的加密功能:
public class EncryptedJsonSerializer extends JsonSerializer<Object> {
/**
* 默认序列化工具对象
*/
private final JsonSerializer<Object> serializer;
public EncryptedJsonSerializer(JsonSerializer<Object> serializer) {
this.serializer = serializer;
}
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
StringWriter stringWriter = new StringWriter();
ObjectCodec objectCodec = jsonGenerator.getCodec();
JsonGenerator nestedGenerator = null;
//空对象或空字符串不处理。
if(o == null || Strings.isNullOrEmpty(String.valueOf(o))){
if (serializer == null) {
serializerProvider.defaultSerializeValue(o, jsonGenerator);
}else{
serializer.serialize(o, jsonGenerator, serializerProvider);
}
return;
}
/*
生成一个新的JsonGenerator,用于将o写入。
*/
if(objectCodec instanceof ObjectMapper){
nestedGenerator = ((ObjectMapper) objectCodec).getJsonFactory().createJsonGenerator(stringWriter);
}
if (nestedGenerator == null) {
throw new NullPointerException("nestedGenerator == null");
}
/*
将数据写入到新生成的JsonGenerator中
*/
if (serializer == null) {
serializerProvider.defaultSerializeValue(o, nestedGenerator);
}else{
serializer.serialize(o, nestedGenerator, serializerProvider);
}
nestedGenerator.close();
/*
JsonGenerator会生成一个带双引号的字符串, 将数据加密后写入。
*/
String value = stringWriter.getBuffer().toString();
try {
//空字符串不加密
jsonGenerator.writeString(AESTools.encrypt(value));
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
}
EncryptedJsonSerializer的处理是先判断是否为空对象或空字符串,如果是那就将它们直接收入到jsonGenerator,否则的话创建一个新的nestedGenerator并将数据写入进去,然后再拿出来进行AES加密,最后写入到原始的jsonGenerator中,这样就完成了属性的加密以久序列化工作。