Spring mvc framework adds encryption/decryption of messages

 In development, we will encounter requirements for encrypted transmission of data transmitted by http. We can rewrite the MappingJackson2HttpMessageConverter class of spring mvc to support encryption and decryption. code show as below:

package com.hzwei.spring.messageconvert.ext;

import java.io. *;
import java.lang.reflect.Type;

import org.springframework.http.*;
import org.springframework.http.converter*;
import org.springframework.http.converter.json.*;
import org.springframework.util.TypeUtils;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.core.util.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.jsecode.platform.util.EncryptDecrypt;

/**
 * Extend the MappingJackson2HttpMessageConverter class to add decryption/encryption support for messages (support spring-web-4.3.x)
 */
public class EncryptMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    private static final MediaType TEXT_EVENT_STREAM = new MediaType("text", "event-stream");
    private PrettyPrinter ssePrettyPrinter;
    private boolean needEncryptOutputMessage = false;
    private boolean needDecryptInputMessage = false;

    protected void init(ObjectMapper objectMapper){
        super.init(objectMapper);

        DefaultPrettyPrinter prettyPrinter = new DefaultPrettyPrinter();
        prettyPrinter.indentObjectsWith(new DefaultIndenter("  ", "\ndata:"));
        this.ssePrettyPrinter = prettyPrinter;
    }

    public boolean isNeedEncryptOutputMessage(){
        return needEncryptOutputMessage;
    }

    public void setNeedEncryptOutputMessage(boolean needEncryptOutputMessage){
        this.needEncryptOutputMessage = needEncryptOutputMessage;
    }

    public boolean isNeedDecryptInputMessage(){
        return needDecryptInputMessage;
    }

    public void setNeedDecryptInputMessage(boolean needDecryptInputMessage){
        this.needDecryptInputMessage = needDecryptInputMessage;
    }

    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException{
        JavaType javaType = getJavaType(clazz, null);
        return readJavaType(javaType, inputMessage);
    }

    public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException{
        JavaType javaType = getJavaType(type, contextClass);
        return readJavaType(javaType, inputMessage);
    }

    private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage){
        try{
            if(inputMessage instanceof MappingJacksonInputMessage){
                Class<?> deserializationView = ((MappingJacksonInputMessage)inputMessage).getDeserializationView();
                if(deserializationView != null){
                    return this.objectMapper.readerWithView(deserializationView)
                                            .forType(javaType)
                                            .readValue(getInputStreamData(inputMessage.getBody()));
                }
            }
            return this.objectMapper.readValue(getInputStreamData(inputMessage.getBody()), javaType);
        }catch(JsonProcessingException ex){
            throw new HttpMessageNotReadableException("JSON parse error: " + ex.getOriginalMessage(), ex);
        }catch(IOException ex){
            throw new HttpMessageNotReadableException("I/O error while reading input message", ex);
        }
    }

    private byte[] getInputStreamData(InputStream in) throws IOException{
        ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int offset = 0;
        while((offset = in.read(buff)) > 0){
            swapStream.write(buff, 0, offset);
        }

        byte[] bytes = swapStream.toByteArray();
        if(needDecryptInputMessage){
            // Decrypt bytes
            bytes = EncryptDecrypt.AESFastDecrypt2(bytes); //TODO replace your decryption algorithm here
        }
        return bytes;
    }

    @Override
    protected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException{
        if(needEncryptOutputMessage){
            writeInternalEncrypt(object, type, outputMessage);
        }else{
            super.writeInternal(object, type, outputMessage);
        }
    }

    // Serialize the return value and encrypt it with AES
    protected void writeInternalEncrypt(Object object, Type type, HttpOutputMessage outputMessage)
            throws IOException, HttpMessageNotWritableException{
        MediaType contentType = outputMessage.getHeaders().getContentType();
        JsonEncoding encoding = getJsonEncoding(contentType);
        JsonFactory factory = this.getObjectMapper().getFactory();
        ByteArrayBuilder bb = new ByteArrayBuilder(factory._getBufferRecycler());
        JsonGenerator generator = factory.createGenerator(bb, encoding);
        writeToJsonGenerator(object, type, generator, contentType);

        try{
            //TODO replace your own encryption algorithm here
            byte[] byteData = EncryptDecrypt.AESFastEncrypt(bb.toByteArray());
            outputMessage.getBody().write(byteData);
        }catch(Exception ex){
            throw new HttpMessageNotWritableException("Could not encrypt and write message: " + ex.getMessage(), ex);
        }
    }

    // The serialization method to keep the original taste of MappingJackson2HttpMessageConverter
    protected void writeToJsonGenerator(Object object, Type type, JsonGenerator generator, MediaType contentType)
            throws IOException, HttpMessageNotWritableException{
        try{
            writePrefix(generator, object);

            Class<?> serializationView = null;
            FilterProvider filters = null;
            Object value = object;
            JavaType javaType = null;
            if(object instanceof MappingJacksonValue){
                MappingJacksonValue container = (MappingJacksonValue)object;
                value = container.getValue();
                serializationView = container.getSerializationView();
                filters = container.getFilters();
            }
            if(type != null && value != null && TypeUtils.isAssignable(type, value.getClass())){
                javaType = getJavaType(type, null);
            }
            ObjectWriter objectWriter;
            if(serializationView != null){
                objectWriter = this.objectMapper.writerWithView(serializationView);
            }else if(filters != null){
                objectWriter = this.objectMapper.writer(filters);
            }else{
                objectWriter = this.objectMapper.writer();
            }
            if(javaType != null && javaType.isContainerType()){
                objectWriter = objectWriter.forType(javaType);
            }
            SerializationConfig config = objectWriter.getConfig();
            if(contentType != null && contentType.isCompatibleWith(TEXT_EVENT_STREAM)
                    && config.isEnabled(SerializationFeature.INDENT_OUTPUT)){
                objectWriter = objectWriter.with(this.ssePrettyPrinter);
            }
            objectWriter.writeValue(generator, value);

            writeSuffix(generator, object);
            generator.flush();

        }catch(JsonProcessingException ex){
            throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getOriginalMessage(), ex);
        }
    }
}

   In the spring mvc project, replace MappingJackson2HttpMessageConverter with EncryptMappingJackson2HttpMessageConverter

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326019361&siteId=291194637