美文网首页Vue
VUE和Spring Boot前后端交互报文加密

VUE和Spring Boot前后端交互报文加密

作者: 天生小包 | 来源:发表于2021-02-02 10:32 被阅读0次

    背景

    公司在做研发平台,提供给金融行业业务系统使用,涉及敏感数据较多,需要对前后端数据交互报文进行加密处理。本文采用目前较流行的方式AES + BASE64算法进行加密。
    VUE端采用:cryptoJS
    Spring Boot端采用:hutool(糊涂)开源工具

    1、VUE

    1.1、引入cryptoJS
    npm install crypto-js --save-dev
    
    1.2、编写加密/解密函数
    //前后端定义的密钥,AES使用16位
    const CRYPTOJSKEY= "ABCDEFG";
    // 加密
    function encrypt(plaintText) {
        var plaintText = plaintText;
        var options = {
            mode: CryptoJS.mode.ECB,
            padding: CryptoJS.pad.Pkcs7
        };
      var key = CryptoJS.enc.Utf8.parse(CRYPTOJSKEY);
      var encryptedData = CryptoJS.AES.encrypt(plaintText, key, options);
      var encryptedBase64Str = encryptedData.toString().replace(/\//g, "_");
      encryptedBase64Str = encryptedBase64Str.replace(/\+/g,"-");
      return encryptedBase64Str;
    }
    
    //解密
    function decrypt(encryptedBase64Str) {
      var vals = encryptedBase64Str.replace(/\-/g, '+').replace(/_/g, '/');
      var options = {
          mode: CryptoJS.mode.ECB,
          padding: CryptoJS.pad.Pkcs7
      };
      var key = CryptoJS.enc.Utf8.parse(CRYPTOJSKEY);
      var decryptedData = CryptoJS.AES.decrypt(vals, key, options);
      var decryptedStr = CryptoJS.enc.Utf8.stringify(decryptedData);
      return decryptedStr
    }  
    
    1.3、前端数据进行加密

    采用请求前拦截操作,encrypt(JSON.stringify(config.data))

    // request interceptorinstance.interceptors.request.use(  
    config => {   
     // Do something before request is sent    
    if (store.getters.token) {      
      config.headers["Authorization"] = `Bearer ${getToken()}`; 
    // 让每个请求携带token-- ['X-Token']为自定义key 请根据实际情况自行修改    }    config.data = encrypt(JSON.stringify(config.data))    
    return config; 
     },  error => {   
     // Do something with request error    
    Message.error("对不起,出错了");    
    console.log(error); 
    // for debug   
     Promise.reject(error); 
     }
    );
    
    1.4、Axios请求加密
    export const createAPI = (url, method, data) => {    
      let config = {}; 
     if (method === "get") {    
        config.params = data;
      } else {    
      config.data = data;
      } 
     config.headers = {  
      "Content-Type": "application/json;charset=UTF-8" 
     };  
     return instance({    
      url,    
      method,    
      ...config  
      });
    };
    

    2、Spring Boot

    2.1、引入hutool
    <!--糊涂工具 https://github.com/looly/hutool/-->        
    <dependency>            
      <groupId>cn.hutool</groupId>            
      <artifactId>hutool-all</artifactId>            
      <version>5.4.7</version>        
    </dependency>
    
    2.2、处理方案(即拦截)定位

    分析:
    1、数据加密/解密主要是针对请求/响应的body进行处理。
    2、springboot前后端对接点为Controler。
    3、如何实现在Controler接收前就实现拦截处理呢?

    方案一

    可以采用拦截器、过滤器等方案。

    方案二、(采用方案)

    1、采用才做 RequestBodyAdvice与ResponseBodyAdvice

    RequestBodyAdvice:

    package org.springframework.web.servlet.mvc.method.annotation;
     
    import java.io.IOException;
    import java.lang.reflect.Type;
    import org.springframework.core.MethodParameter;
    import org.springframework.http.HttpInputMessage;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.lang.Nullable;
     
    public interface RequestBodyAdvice {
        boolean supports(MethodParameter var1, Type var2, Class<? extends HttpMessageConverter<?>> var3);
     
        HttpInputMessage beforeBodyRead(HttpInputMessage var1, MethodParameter var2, Type var3, Class<? extends HttpMessageConverter<?>> var4) throws IOException;
     
        Object afterBodyRead(Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<? extends HttpMessageConverter<?>> var5);
     
        @Nullable
        Object handleEmptyBody(@Nullable Object var1, HttpInputMessage var2, MethodParameter var3, Type var4, Class<? extends HttpMessageConverter<?>> var5);
    }
    

    ResponseBodyAdvice:

    package org.springframework.web.servlet.mvc.method.annotation;
    
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.lang.Nullable;
    
    public interface ResponseBodyAdvice<T> {
       boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
    
       @Nullable
       T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
    }
    

    在beforeBodyRead和beforeBodyWrite两个接口中有接收和返回报文,可以在里面实现报文解密加密。

    自定义请求实现类DecryptRequestBodyAdvice.java:

    /**
     * 请求数据接收处理类<br>
     *
     * 对加了@Decrypt的方法的数据进行解密操作<br>
     *
     * 只对 @RequestBody 参数有效
     * @author xxm
     */
    @ControllerAdvice
    public class DecryptRequestBodyAdvice implements RequestBodyAdvice {
     
        @Value("${crypto.charset}")
        private String charset = "UTF-8";
        @Value("${crypto.key}")
        private String key;
     
        @Override
        public boolean supports(MethodParameter methodParameter, Type targetType,
                                Class<? extends HttpMessageConverter<?>> converterType) {
            return true;
        }
     
        @Override
        public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
                                      Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
            return body;
        }
     
        @Override
        public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
                                               Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
    //        if( NeedCrypto.needDecrypt(parameter) ){
    //            return new DecryptHttpInputMessage(inputMessage , charset , key);
    //        }
    //        return inputMessage;
            return new DecryptHttpInputMessage(inputMessage , charset , key);//请求信息解密,参考DecryptHttpInputMessage解密类
        }
     
        @Override
        public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
                                    Class<? extends HttpMessageConverter<?>> converterType) {
            return body;
        }
    }
    

    自定义响应实现类EncryptResponseBodyAdvice.java:

    /**
     * 请求响应处理类<br>
     *
     * 对加了@Encrypt的方法的数据进行加密操作
     *
     * @author 熊诗言
     *
     */
    @ControllerAdvice
    public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
     
        @Value("${crypto.charset}")
        private String charset = "UTF-8";
        @Value("${crypto.key}")
        private String key;
     
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return NeedCrypto.needEncrypt(returnType);
        }
     
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
            //TODO 实现具体的加密方法
            System.out.println("________________");
     
            return body;
        }
     
    }
    
    

    注:必须加入@ControllerAdvice注解,没有它是没有注入到spring中的。

    自定义请求信息解密类,DecryptHttpInputMessage.java:

    /**
     *
     * @author xxm
     */
    public class DecryptHttpInputMessage implements HttpInputMessage {
        private HttpInputMessage inputMessage;
        private String charset;
        private String key;
     
        public DecryptHttpInputMessage(HttpInputMessage inputMessage, String charset , String key) {
            this.inputMessage = inputMessage;
            this.charset = charset;
            this.key = key;
        }
     
        @Override
        public InputStream getBody() throws IOException {
            //使用hutool开始解密
            String content = IoUtil.read(inputMessage.getBody() , charset);
            byte[] bytes = SecureUtil.aes(key.getBytes(charset)).decrypt(content);
     
            return new ByteArrayInputStream(bytes);
        }
     
        @Override
        public HttpHeaders getHeaders() {
            return inputMessage.getHeaders();
        }
    

    相关文章

      网友评论

        本文标题:VUE和Spring Boot前后端交互报文加密

        本文链接:https://www.haomeiwen.com/subject/hunetltx.html