VUE+SpringBoot实现传输加密

背景

前段时间公司做项目,该项目涉及到的敏感数据比较多,经过的一波讨论之后,决定前后端进行接口加密处理,采用的是 AES + BASE64 算法加密~

为实现AES,自然而然想到“crypto”。上网查询相关的技术或工具,一大堆最后自我选型前端采用cryptoJS、后端使用了hutool(糊涂)开源工具,接下来上手开干。

 

前端


1、引入cryptoJS

npm install crypto-js --save-dev

2、编写加密/解密函数

const CRYPTOJSKEY= "afDeffjlj0343jjF";//前后端定义的密钥,AES使用16位
// 加密
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
}

3、前端数据加密

注:根据自身项目选择使用数据加密代码

我采用请求前拦截操作,encrypt(JSON.stringify(config.data))部分为使用代码 ,如下:

// request interceptor
instance.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);
  }
);

4、axios请求

注:根据自身编写axios请求,但请求头中Content-Type需为:application/json;charset=UTF-8

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
  });
};

5、请求结果

后端


1、引入hutool

本人是Maven项目,即在pom.xml添加

<!--糊涂工具 https://github.com/looly/hutool/-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.7</version>
        </dependency>

2、处理方案(即拦截)定位

分析:

1、数据加密/解密主要是针对请求/响应的body进行处理。

2、springboot前后端对接点为Controler。

3、如何实现在Controler接收前就实现拦截处理呢?

方案一、

提及到拦截大多数人会想到拦截器、过滤器,在此不做采用方案。

方案二、(采用方案)

1、提及到body,spring中有没有相关的类就能操作body呢?

答案:RequestBodyAdvice与ResponseBodyAdvice

我们来看看这2个类源码

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这2个接口了吧!开不开心正式我们想要的东西。那我来实现他吧:

自定义请求实现类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();
    }

}
Logo

前往低代码交流专区

更多推荐