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

时间:2021-7-3 作者:qvyue

背景

公司在做研发平台,提供给金融行业业务系统使用,涉及敏感数据较多,需要对前后端数据交互报文进行加密处理。本文采用目前较流行的方式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
        
cn.hutoolhutool-all5.4.7
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 {
   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:

/**
 * 请求数据接收处理类
* * 对加了@Decrypt的方法的数据进行解密操作
* * 只对 @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:

/**
 * 请求响应处理类
* * 对加了@Encrypt的方法的数据进行加密操作 * * @author 熊诗言 * */ @ControllerAdvice public class EncryptResponseBodyAdvice implements ResponseBodyAdvice { @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();
    }
声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。