統一處理接口加解密


在這裏插入圖片描述

對於安全性較高的項目來說,對請求數據和返回數據進行加解密是有必要的。

思路

利用@ControllerAdvice註解,攔截請求和返回數據進行加解密,對前端傳過來的參數進行解密,對接口返回數據進行加密。

我的加解密具體流程,主要是和客服端交互,如果是和安卓和iOS交互的注意密鑰的一致性,加解密的過程一定不能錯

  • 服務端流程 業務數據json串----》經AES加密後----》再BASE64編碼-----》gzip壓縮
  • 客服端流程 base64編碼(decode)----》gzip解壓----》解密------》json串

我的工具類


import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author someone
 * @create 2019-08-10 9:44
 **/
public class AESUtil {


    /**
     * AES加密字符串
     *
     * @param content 需要被加密的字符串
     * @return 密文
     */
    public static String encrypt(String content) {
        try {
            IvParameterSpec zeroIv = new IvParameterSpec(SecretProperties.API_SECRET.getBytes());
            SecretKeySpec key = new SecretKeySpec(SecretProperties.API_SECRET.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
            byte[] encryptedData = cipher.doFinal(content.getBytes("UTF-8"));
           /* String gzi = Base64.encode(encryptedData);
            byte[] gzis = GZIPUtils.compress(gzi);*/
            return Base64.encode(encryptedData);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解密AES加密過的字符串
     *
     * @param content AES加密過過的內容
     * @return 明文
     */
    public static String decrypt(String content) {
        try {
            byte[] byteMi = Base64.decode(content);
//			byte[] byteMi=	str2ByteArray(content);
            IvParameterSpec zeroIv = new IvParameterSpec(SecretProperties.API_SECRET.getBytes());
            SecretKeySpec key = new SecretKeySpec(SecretProperties.API_SECRET.getBytes(), "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key,zeroIv);
            byte[] decryptedData = cipher.doFinal(byteMi);
            /* String decry = GZIPUtils.uncompressToString(decryptedData);*/
            return new String(decryptedData,"UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String getSign(String timeStamp, String params) {
        return getMD5(timeStamp + SecretProperties.API_KEY + params).toUpperCase();
    }


    public static void main(String[] args) {

        String content = "{\"id\":\"src_1EWhwuGMfj37P6Rx3INyzLLW\",\"expMonth\":\"01\"," +
                "\"expYear\":\"22\"}";

      /*  String content = "{\"userName\":\"15260767080\"}";*/
        // 加密
        String encrypt = AESUtil.encrypt(content);
        String cc ="";
        System.out.println("gzip"+GZIPUtils.uncompressToString(Base64.decode(cc)));
        String decry = AESUtil.decrypt(GZIPUtils.uncompressToString(Base64.decode(cc)));
        String sign = AESUtil.getSign(getTimeStamp(),encrypt);
        System.out.println("sign"+sign);
        System.out.println("加密:" + encrypt);
        System.out.println("解密:" + decry);
    }

    //對字符串md5加密
    public static String getMD5(String str) {
        byte[] hash = null;
        try {
            hash = MessageDigest.getInstance("MD5").digest(str.getBytes("UTF-8"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) hex.append("0");
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }


    public static String getTimeStamp() {
        DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
        String timeStamp = df.format(new Date());
        System.out.println(timeStamp);
        return "20190411191250";
    }

    /**
     * 字符串轉時間
     * @param strDate
     * @return
     */
    public static  Date dateFormat(String strDate){
        //注意:SimpleDateFormat構造函數的樣式與strDate的樣式必須相符
        SimpleDateFormat sDateFormat=new SimpleDateFormat("yyyyMMddHHmmss"); //加上時間
        Date date= null;
        //必須捕獲異常
        try {
            date=sDateFormat.parse(strDate);

            System.out.println(date);
        } catch(
                ParseException px) {
            px.printStackTrace();
        }
        return date;
    }

    // 兩個日期相減得到的毫秒數
    public static long dateDiff(Date beginDate, Date endDate) {
        long date1ms = beginDate.getTime();
        long date2ms = endDate.getTime();
        return date2ms - date1ms;
    }
}


接下來主要是用spring的註解來攔截你的請求數據和服務端返回數據,對攔截到數據進行加解密,在這裏我們主要是用@ControllerAdvice(關於這個註解的含義和用法可以自行去了解),這個註解的主要作用是去攔截你的controlle的所有請求
在你的實現類裏面繼承requestBodyAdviceAdapter的方法,攔截你的請求數據

@Slf4j
@ControllerAdvice
@ConditionalOnProperty(prefix = "faster.secret", name = "enabled", havingValue = "true")
@EnableConfigurationProperties({SecretProperties.class})
@Order(1)
public class DecryptRequestBodyAdvice extends RequestBodyAdviceAdapter {
    @Autowired
    private SecretProperties secretProperties;

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

然後再新建一個類,繼承ResponseBodyAdvice,攔截返回數據

@Slf4j
@ControllerAdvice
@ConditionalOnProperty(prefix = "faster.secret", name = "enabled", havingValue = "true")
@EnableConfigurationProperties({SecretProperties.class})
@Order(2)
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {

    @Autowired
    private SecretProperties secretProperties;


    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }

在開發中也遇到了一些坑,現在已經在實際的項目中應用,只貼出來了部分代碼,如果遇到什麼問題可以留言一起解決

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章