Jackson custom serialization and deserialization annotation encryption and decryption fields

Jackson custom serialization and deserialization annotation encryption and decryption fields

1 Problem Scenario

In some scenarios, database fields are used to store json format data. For security reasons, some sensitive information fields in the json data need to be encrypted and stored, such as ID number, mobile phone number, etc. If the entire json data is encrypted, the encrypted data will be larger. It is more practical to only encrypt the fields that need to be decrypted.

How to achieve this?

I have written a desensitization annotation before, which was implemented through custom serialization. Looking at it together, it is actually possible to reuse the previous desensitization annotation mode, define field encryption and decryption annotations, and implement it through custom serialization and deserialization.

2 code implementation

  • Annotation class
import com.test.jsonencrypt.EncryptJsonFiledDeSerializer;
import com.test.jsonencrypt.EncryptJsonFiledSerializer;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <pre>
 * 加解密注解,标注在属性上
 * </pre>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@JacksonAnnotationsInside
// 使用自定义的序列化实现
@JsonSerialize(using = EncryptJsonFiledSerializer.class)
// 使用自定义的序列化实现
@JsonDeserialize(using = EncryptJsonFiledDeSerializer.class)
public @interface EncryptJsonFiled {
    
    

}
  • Custom serialization implementation class
import com.test.jsonencrypt.annotation.EncryptJsonFiled;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.test.util.EncryUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Objects;

/**
 * <pre>
 * 序列化注解自定义实现
 * JsonDeserializer<String>:指定String类型,deserialize()方法用于将修改后的数据载入
 * Jackson使用ContextualSerializer在序列化时获取字段注解的属性
 * </pre>
 */
public class EncryptJsonFiledDeSerializer extends JsonDeserializer<String> implements ContextualDeserializer {
    
    

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
    
    
        String valueAsString = p.getValueAsString();
        // 数据不为空时解密数据
        if (StringUtils.isNotBlank(valueAsString)) {
    
    
            // EncryUtil为封装的加解密工具
            return EncryUtil.decrypt(valueAsString);
        }
        return valueAsString;
    }

    @Override
    public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
    
    
        EncryptJsonFiled annotation = property.getAnnotation(EncryptJsonFiled.class);
        // 只针对String类型
        if (Objects.nonNull(annotation)&&Objects.equals(String.class, property.getType().getRawClass())) {
    
    
            return this;
        }
        return ctxt.findContextualValueDeserializer(property.getType(), property);
    }
}
  • Custom deserialization implementation class
import com.test.jsonencrypt.annotation.EncryptJsonFiled;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.test.util.EncryUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Objects;

/**
 * <pre>
 * 序列化注解自定义实现
 * JsonSerializer<String>:指定String类型,serialize()方法用于将修改后的数据载入
 * Jackson使用ContextualSerializer在序列化时获取字段注解的属性
 * </pre>
 */
public class EncryptJsonFiledSerializer extends JsonSerializer<String> implements ContextualSerializer {
    
    

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
    
    
        // 数据不为空时加密数据
        if (StringUtils.isNotBlank(value)) {
    
    
            String encrypt = EncryUtil.encry(value);
            gen.writeString(encrypt);
            return;
        }
        gen.writeString(value);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
    
    
        EncryptJsonFiled annotation = property.getAnnotation(EncryptJsonFiled.class);
        // 只针对String类型
        if (Objects.nonNull(annotation)&&Objects.equals(String.class, property.getType().getRawClass())) {
    
    
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }

}

3 tests

  • Simple entity class for testing
@Data
@Accessors(chain = true)
public class User {
    
    

    private String name;
    
    private int age;
    
    @EncryptJsonFiled
    private String idCard;

}
  • Test class
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * <pre>
 * 测试加解密注解
 * </pre>
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestEntry {
    
    

    @Test
    public void test() throws JsonProcessingException {
    
    
        User user = new User().setAge(11)
                .setName("小明")
                .setIdCard("13523451124");
        // 注意使用Jackson的json工具
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(user);
        System.out.println("序列化后的json数据:" + json);

        User user1 = objectMapper.readValue(json, User.class);
        System.out.println("反序列化后的解密数据:" + user1.getIdCard());
    }

}

Test Results:

序列化后的json数据:{
    
    "name":"小明","age":11,"idCard":"2f3d8f692eeaac2cbc60423ed99aed63"}
反序列化后的解密数据:13523451124

Guess you like

Origin blog.csdn.net/weixin_46505978/article/details/128577970