使用Jackson加密/解密JSON字段

Jackson是Java平臺很是流行的JSON解析框架,並且擴展性很強,本文經過添加一個Module來增強JSON字段的解析來實現單個字段的加密與解密功能。java

基本想法是使用Jackson來處理自定義的註解,在須要加密/解密的字段上添加上相應的註解,先定義個自定義的註解:git

@JacksonAnnotation
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Encrypt {}
複製代碼

而後在須要在POJO中對字段添加@Encrypt註解:github

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:json

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類,EncryptionModulesetupModule方法中添加了一個序列化修改器和反序列化修改器,EncryptedSerializerModifier重載changeProperties方法,遍歷屬性若是屬性有@Encrypt註解那麼須要將屬性的JsonSerializer設置爲EncryptedJsonSerializerapp

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中,這樣就完成了屬性的加密以久序列化工做。ide

參考資源

相關文章
相關標籤/搜索