RequestBodyAdvice和ResponseBodyAdvice使用完成入參解密和返回加密

模擬項目中使用RequestBodyAdvice對前端傳入的數據進行解密(入參),請求成功之後使用ResponseBodyAdvice對返回值進行加密處理

注意點:分別需要實現接口

RequestBodyAdvice 和 ResponseBodyAdvice,需要配合註解@ControllerAdvice使用

特別需要注意的是,針對RequestBodyAdvice僅作用在請求參數有註解@RequestBody的,同樣的ResponseBodyAdvice僅對有註解@ResponseBody生效

 

下面的代碼演示:

新建requestBody和responseBody攔截類:

package com.wm.mi.config;

import com.wm.mi.exception.MyException;
import com.wm.mi.util.HttpInputMessageUtil;
import com.wm.mi.util.Md5Tool;
import com.wm.mi.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.List;

/***
 * @ClassName: DecodeRequestBody
 * @Description: 請求解密  注意:RequestBodyAdvice僅針對打上@RequestBody的註解生效
 * @Author: wm_yu
 * @Create_time: 14:43 2019-11-11
 */
@Slf4j
@Component
@ControllerAdvice("com.wm.mi.controller")
public class DecodeRequestBody implements RequestBodyAdvice {

    @Value("${enctry.secret}")
    private String SECRET;

    private static final String SECRET_KEY = "SECRET_KEY";

    /**
     *  設置條件,這個條件爲true纔會執行下面的beforeBodyRead方法
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return true;
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) throws IOException {
        List<String> headerList = request.getHeaders().get(SECRET_KEY);
        if(CollectionUtils.isEmpty(headerList) || StringUtils.isEmpty(headerList.get(0)) || !SECRET.equals(headerList.get(0))){
            throw new MyException("request header no access key....");
        }
        InputStream inputStream = request.getBody();
        String requestBodyStr = RequestUtil.getRequestBodyStr(inputStream);
        log.info("start decode request body:{}",requestBodyStr);
        if(StringUtils.isEmpty(requestBodyStr)){
            return request;
        }
        String decodeStr = null;
        try {
            decodeStr = Md5Tool.md5Decode(requestBodyStr, SECRET);
            log.info("end decode request body:{}",decodeStr);
        } catch (Exception e) {
            throw new MyException("decode request body exception....");
        }
        return HttpInputMessageUtil.builder().headers(request.getHeaders()).body(new ByteArrayInputStream(decodeStr.getBytes("UTF-8"))).build();
    }

    @Override
    public Object afterBodyRead(Object body, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        return body;
    }

    /**
     * 傳入的json是空值的時候,進入這個方法
     * @param o
     * @param httpInputMessage
     * @param methodParameter
     * @param type
     * @param aClass
     * @return
     */
    @Override
    public Object handleEmptyBody(Object o, HttpInputMessage httpInputMessage, MethodParameter methodParameter, Type type, Class<? extends HttpMessageConverter<?>> aClass) {
        log.info("input request body be null......");
        return o;
    }
}

 

 

package com.wm.mi.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.wm.mi.exception.MyException;
import com.wm.mi.util.Md5Tool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/***
 * @ClassName: EncodeResponseBody
 * @Description: 返回加密   注意ResponseBodyAdvice僅針對打上@ResponseBody註解的返回值生效
 * @Author: wm_yu
 * @Create_time: 14:43 2019-11-11
 */
@Slf4j
@Component
@ControllerAdvice("com.wm.mi.controller")
public class EncodeResponseBody implements ResponseBodyAdvice {

    @Value("${enctry.secret}")
    private String SECRET;

    /**
     * 設置條件,這個條件爲true纔會執行下面的beforeBodyWrite方法
     * @param methodParameter
     * @param aClass
     * @return
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    /**
     * 返回值加密處理
     * @param body
     * @param methodParameter
     * @param mediaType
     * @param aClass
     * @param serverHttpRequest
     * @param serverHttpResponse
     * @return
     */
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //禁止fastjson轉換循環引用
        String bodyStr = JSON.toJSONString(body, SerializerFeature.DisableCircularReferenceDetect);
        log.info("start encode response:{}", JSON.toJSONString(body));
        String encodeStr = null;
        try {
            encodeStr = Md5Tool.md5Encode(bodyStr, SECRET);
            log.info("end encode response:{}",encodeStr);
        } catch (Exception e) {
            throw new MyException("encode response body exception...");
        }
        return encodeStr;
    }
}

Yml中自定義的一個 常量:

enctry:
  secret: d5416a341766390368ab75d220a6c051

 

使用到的MD加密解密工具類:

 

 

package com.wm.mi.util;

/***
 * @ClassName: Md5Tool
 * @Description: 簡單MD5加密解密工具類
 * @Author: wm_yu
 * @Create_time: 14:53 2019-11-11
 */

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;

/**
 * @class_name: MD5Tool
 * @description:
 * @author: wm_yu
 * @create: 2019/09/12
 **/
public class Md5Tool {

    private final static Logger log = LoggerFactory.getLogger(Md5Tool.class);
    /**
     * 向量(同時擁有向量和密匙才能解密),此向量必須是8byte,多少都報錯
     */
    private final static byte[] DESIV = new byte[] { 0x22, 0x54, 0x36, 110, 0x40, (byte) 0xac, (byte) 0xad, (byte) 0xdf };
    /**
     * 加密算法的參數接口
     */
    private static AlgorithmParameterSpec iv = null;
    private static Key key = null;
    private static String charset = "utf-8";

   private static void init(String SECRET_KEY){
       try {
           // 設置密鑰參數
           DESKeySpec keySpec = new DESKeySpec(SECRET_KEY.getBytes(charset));
           // 設置向量
           iv = new IvParameterSpec(DESIV);
           // 獲得密鑰工廠
           SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
           // 得到密鑰對象
           key = keyFactory.generateSecret(keySpec);
       } catch (Exception e) {
           e.printStackTrace();
       }
   }



    /**
     * 加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String md5Encode(String data,String SECRET_KEY) throws Exception {
        init(SECRET_KEY);
        // 得到加密對象Cipher
        Cipher enCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        // 設置工作模式爲加密模式,給出密鑰和向量
        enCipher.init(Cipher.ENCRYPT_MODE, key, iv);
        byte[] pasByte = enCipher.doFinal(data.getBytes(charset));
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encode(pasByte);
    }

    /**
     * 解密
     * @param data
     * @return
     * @throws Exception
     */
    public static String md5Decode(String data,String SECRET_KEY) throws Exception {
        init(SECRET_KEY);
        Cipher deCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        deCipher.init(Cipher.DECRYPT_MODE, key, iv);
        BASE64Decoder base64Decoder = new BASE64Decoder();
        //此處注意doFinal()的參數的位數必須是8的倍數,否則會報錯(通過encode加密的字符串讀出來都是8的倍數位,但寫入文件再讀出來,就可能因爲讀取的方式的問題,導致最後此處的doFinal()的參數的位數不是8的倍數)
        //此處必須用base64Decoder,若用data。getBytes()則獲取的字符串的byte數組的個數極可能不是8的倍數,而且不與上面的BASE64Encoder對應(即使解密不報錯也不會得到正確結果)
        byte[] pasByte = deCipher.doFinal(base64Decoder.decodeBuffer(data));
        return new String(pasByte, charset);
    }

    /**
     * 獲取MD5的值,可用於對比校驗
     * @param sourceStr
     * @return
     */
    private static String getMD5Value(String sourceStr) {
        String result = "";
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(sourceStr.getBytes());
            byte b[] = md.digest();
            int i;
            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0){ i += 256;}
                if (i < 16){buf.append("0");}
                buf.append(Integer.toHexString(i));
            }
            result = buf.toString();
        } catch (NoSuchAlgorithmException e) {
        }
        return result;
    }

    public static void main(String[] args) {
        try {
            String secret_key = "d5416a341766390368ab75d220a6c051";
            String source = "name:yu";
            String value = md5Encode(source,secret_key);
            String decode = md5Decode(value,secret_key);
            System.out.println("原始數據:" + source + "----加密後的數據:" + value + "-----解密後的數據:" + decode);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }



}

構建request處理之後的返回數據類:

package com.wm.mi.util;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;

import java.io.IOException;
import java.io.InputStream;

/***
 * @ClassName: HttpInputMessageUtil
 * @Description:
 * @Author: wm_yu
 * @Create_time: 15:32 2019-11-11
 */
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Accessors(fluent = true)
public class HttpInputMessageUtil implements HttpInputMessage {

    private HttpHeaders headers;
    private InputStream body;

    @Override
    public InputStream getBody() throws IOException {
        return body;
    }

    @Override
    public HttpHeaders getHeaders() {
        return headers;
    }

}

獲取request中的數據工具類:

package com.wm.mi.util;

import org.springframework.util.ObjectUtils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/***
 * @ClassName: RequestUtil
 * @Description:
 * @Author: wm_yu
 * @Create_time: 15:16 2019-11-11
 */
public class RequestUtil {


    /**
     * reuqest body流數據轉換爲String
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static String getRequestBodyStr(InputStream inputStream) throws IOException {
        StringBuilder builder = new StringBuilder();
        if (!ObjectUtils.isEmpty(inputStream)) {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            char[] charBuffer = new char[128];
            int bytesRead = -1;
            while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                builder.append(charBuffer, 0, bytesRead);
            }
        }else {
            builder.append("");
        }
        return builder.toString();

    }


}

自定義一個異常類:

package com.wm.mi.exception;

import lombok.AllArgsConstructor;

/***
 * @ClassName: MyException
 * @Description: 自定義異常
 * @Author: wm_yu
 * @Create_time: 15:14 2019-11-11
 */
@AllArgsConstructor
public class MyException extends RuntimeException{

    private Integer errorCode;
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }

    public MyException(Integer errorCode, String message) {
        super(message);
        this.errorCode = errorCode;
    }
    public Integer getErrorCode() {
        return this.errorCode;
    }

    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }

}

新建cotroller測試類:

package com.wm.mi.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/***
 * @ClassName: HelloController
 * @Description:
 * @Author: wm_yu
 * @Create_time: 16:35 2019-11-11
 */
@RestController
@RequestMapping("/yu")
public class HelloController {

    @GetMapping("/hello")
    public String hello(@RequestBody String name){
        return String.format("hell0,%s",name);
    }
}

 

先使用工具類,構建一個加密之後的請求參數,如下

模擬請求:

結果如下:

 

 

 

發佈了93 篇原創文章 · 獲贊 26 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章