欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot接口加密與解密的實現

 更新時間:2023年10月27日 09:44:56   作者:Archie_java  
這篇文章主要介紹了SpringBoot接口加密與解密的實現

一、對稱/非對稱加密

1、簡介

對稱加密只有一個秘鑰,加密和解密都是用同一個秘鑰,所以叫做對稱加密。

非對稱加密有兩個秘鑰,一個是公鑰,一個是私鑰。非對稱的特點在于,公鑰加密的私鑰可以解密,但私鑰加密的,公鑰解不出來,只能驗證是否由私鑰進行加密

目前常見的加密方式是有兩種,一種是對稱加密(AES為代表),一種是非對稱加密(RSA為代表)

2、RSA和AES介紹

2.1 RSA

特點:只需交換公鑰;公/秘鑰機制,公鑰加密,私鑰解密(或者私鑰加密,公鑰解密);公鑰負責加密,私鑰負責解密;私鑰負責簽名,公鑰負責驗證

缺點:加解密速度慢,特別是解密

2.2 AES

特點:加解密用同一秘鑰

優(yōu)點:速度快,效率高;

缺點:秘鑰交換問題

3、RSA/AES組合

對稱加密(AES)的優(yōu)勢在于加密較快,**但**劣勢在于秘鑰一旦給出去就不安全了。非對稱加密(RSA)的優(yōu)勢在于安全,就算提供公鑰出去,別人也解密不了數據,但劣勢是加密速度較慢

實際使用的過程中常常將兩者組合使用(AES+RSA),這樣可以安全的傳輸AES秘鑰,避免了RSA加密的慢速度

  • 生成一個隨機AES秘鑰字符串
  • 使用RSA公鑰加密AES秘鑰,然后再用AES秘鑰加密真正的內容
  • 把skey=加密的AES秘鑰,body=AES秘鑰加密的內容傳過去
  • 對面使用RSA私鑰解密AES秘鑰,然后用AES秘鑰解密出內容

4、Base64編碼的作用

加密后的數據可能不具備可讀性,因此我們一般需要對加密后的數據再使用 Base64 算法進行編碼,獲取可讀字符串。換言之,AES 或者RSA加密方法的返回值是一個 Base64 編碼之后的字符串,AES或者RSA 解密方法的參數也是一個 Base64 編碼之后的字符串,先對該字符串進行解碼,然后再解密。

二、Java實現加解密/加驗簽

1、全局Config

public class Config {

    public static final String AES_ALGORITHM = "AES/CBC/PKCS5Padding";
    public static final String RSA_ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
    //必須是PKCS8格式
    public static final String CLIENT_PRIVATE_KEY = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAO/8ucCgOTJ7DCPC" +
            "rCCL1VKDnUX61QnxwbAvpGp1/lletEIcjUouM7F0VvMHzViNLvpw7N7NBHPa+5gO" +
            "js68t9hKMUh+a6RTE34SWIqSDRPCzDKVWugsFb04o3vRl3rZ1z6B+QDdW7xwOhEr" +
            "PPoEqmjjIOjQPcU6xs0SPzSimOa1AgMBAAECgYAO5m0OBaSnerZNPhf7yVLMVbmd" +
            "D67MeEMjUkHuDjdlixi8BhPLqESzXtrLKg/Y0KM7D2nVh3sgSldWoIjDUzpCx8Z2" +
            "yHLU1K2wakMdBgEF3xeJPxxZRpP+earl0SyLTA4hMxl48uAjn/mkPgzoMgQkqyQz" +
            "5HOWjjsCLJFyEvqmoQJBAP5cBk0KXpHnCMgOupbi/pXDyaF1o+dCE97GaEdrV/0P" +
            "uwDfYDYfY3wzd1QM7C4b4MmE+SNVpC0W9PyaMONJlN0CQQDxiPiGdwX9actMNJea" +
            "JZ+k3BjCN+mM6Px7j/mtYcXWNZkyCXSXUBI62drZ0htenrh2qwichMlMgNJClvG6" +
            "Gu+5AkEA30R7q2gstrkrNh/nnMZHXcJr3DPc2QNhWayin/4TT+hc51krpJZMxxqN" +
            "5dMqBRcnavwzi9aCs6lxBcF6pCdUaQJANhd7uPls4PzRZ6abkQz9/LjB3rUQ29rN" +
            "uIpc2yR7XuawAVG2x7BJ9N4XMhLoyD75hrH1AsCGKFjtPbZ6OjiQGQJAF2DbIodC" +
            "uYb6eMZ8ux1Ab0wBEWWc5+iGgEVBNh22uZ/klE1/C0+KKzZhqgzaA/vPapq6dhuJ" +
            "sNXlJia10PwYrQ==";

    public static final String CLIENT_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDv/LnAoDkyewwjwqwgi9VSg51F" +
            "+tUJ8cGwL6Rqdf5ZXrRCHI1KLjOxdFbzB81YjS76cOzezQRz2vuYDo7OvLfYSjFI" +
            "fmukUxN+EliKkg0TwswylVroLBW9OKN70Zd62dc+gfkA3Vu8cDoRKzz6BKpo4yDo" +
            "0D3FOsbNEj80opjmtQIDAQAB";

    public static final String SERVER_PRIVATE_KEY = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAPGkxlAJPKR3BRxT" +
            "PIeB3pDv117j8XbpuEik5UIOlY3GUtAV1sad5NNDUAnP/DB80yAQ8ycm9Xdkutuo" +
            "f25Xlb7w0bRQNpfJlijx9eF8PsB6t63r8KAfWJlqbNHgN8AMK9P5XzVyN4YiEnUl" +
            "Jh/EYiwLiYzflNnmnnfRrI4nUo8fAgMBAAECgYEAvwTxm81heeV4Tcbi33/jUBG4" +
            "4BMzCzyA6DQp4wkiYju3tTS+Xq3seLEKcWdPxYi3YO7lODsM6j/fksrlSXXFMe1i" +
            "ZAF3FNuDVZPz2zdFYS8vh6kdlDHMJAUnU/POMMWJ880MQDtkwTuzH8Tao8OKcAP4" +
            "kc0QuG00wOrmuE+5gZECQQD9bqZkJsN+tj3+pxs57azy6B6gOqgm54/ujB+u63XU" +
            "rO9Sf57asgF4OfUFltaVhjlUMSrWcgp6f4HSy7hBSKJpAkEA9BeML5iDIHOgTIws" +
            "+ID55ELbzO7A/YtcYnUU09mkKCdonMXbXke+EhLApf5vX9ZmreoEfJCdsTnMEcQi" +
            "fkjkRwJBALpf2TXl2/cfhs/zjG45f+rTEVK8UFTsDklb+yDkQC87TnTZLbWfGr2T" +
            "wcFugDhOEXL9BYfXLiWQB6VB9Crug6ECQGEmTiFTbj0oSBCvaeauTsdO5PS3whAn" +
            "u2lkeBmpcfCZXsWm6hyoKTpARHTMw789Mjjd/1Mkq96xxkr76U6h7FkCQHRc2elg" +
            "Dh84wqHIptwa+moosVvd7aSzktuOB4CQRO10qKkSHVFuI+sl47A4KGzH/nX9ydUm" +
            "tpsTnQAlXwBczd4=";

    public static final String SERVER_PUBLIC_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDxpMZQCTykdwUcUzyHgd6Q79de" +
            "4/F26bhIpOVCDpWNxlLQFdbGneTTQ1AJz/wwfNMgEPMnJvV3ZLrbqH9uV5W+8NG0" +
            "UDaXyZYo8fXhfD7Aeret6/CgH1iZamzR4DfADCvT+V81cjeGIhJ1JSYfxGIsC4mM" +
            "35TZ5p530ayOJ1KPHwIDAQAB";

}

2、RSA非對稱加密

import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import java.security.*;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import org.springframework.util.Base64Utils;

public class RSACipher {

    /**
     * 獲取公鑰
     * @param key 密鑰字符串(經過base64編碼)
     * @return 公鑰
     */
    public static PublicKey getPublicKey(String key) throws Exception {
        // 按照X.509標準對其進行編碼的密鑰
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64Utils.decode(key.getBytes()));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 生成公鑰
        PublicKey publicKey = keyFactory.generatePublic(keySpec);
        return publicKey;
    }

    /**
     * 獲取私鑰
     * @param key 密鑰字符串(經過base64編碼)
     * @return 私鑰
     */
    public static PrivateKey getPrivateKey(String key) throws Exception {
        // 按照PKCS8格式標準對其進行編碼的密鑰,首先要將key進行base64解碼
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64Utils.decode(key.getBytes()));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        // 生成私鑰
        PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
        return privateKey;
    }

    /**
     * 加密方法
     * @param publicKey 公鑰
     * @param raw       待加密明文
     * @return 加密后的密文
     */
    public static byte[] encrypt(String publicKey, byte[] raw) throws Exception {
        Key key = getPublicKey(publicKey);
        Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);
        // 初始化
        cipher.init(Cipher.ENCRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
        byte[] encryption = cipher.doFinal(raw);
        // 最后將加密后的數據進行base64編碼
        return Base64Utils.encode(encryption);
    }

    /**
     * 解密方法
     * @param privateKey 私鑰
     * @param enc  待解密密文
     * @return 解密后的明文
     */
    public static byte[] decrypt(String privateKey, byte[] enc) throws Exception {
        Key key = getPrivateKey(privateKey);
        Cipher cipher = Cipher.getInstance(Config.RSA_ALGORITHM);
        // 初始化
        cipher.init(Cipher.DECRYPT_MODE, key, new OAEPParameterSpec("SHA-256", "MGF1", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT));
        // 先進行base64解密,然后解碼
        return cipher.doFinal(Base64Utils.decode(enc));
    }

    /**
     * 簽名
     * @param privateKey 私鑰
     * @param content    要進行簽名的內容
     * @return 簽名
     */
    public static String sign(String privateKey, byte[] content) {
        try {
            // privateKey進行base64編碼,然后生成PKCS8格式私鑰
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64Utils.decode(privateKey.getBytes()));
            KeyFactory key = KeyFactory.getInstance("RSA");
            PrivateKey priKey = key.generatePrivate(priPKCS8);
            // 簽名摘要算法
            Signature signature = Signature.getInstance("SHA256WithRSA");
            // 用私鑰初始化此對象以進行簽名
            signature.initSign(priKey);
            // 使用指定的字節(jié)數組更新簽名或驗證
            signature.update(content);
            // 獲得簽名字節(jié)
            byte[] signed = signature.sign();
            // 進行base64編碼返回
            return new String(Base64Utils.encode(signed));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 驗簽
     * @param publicKey 公鑰
     * @param content   要驗簽的內容
     * @param sign      簽名
     * @return 驗簽結果
     */
    public static boolean checkSign(String publicKey, byte[] content, String sign) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            // 進行base64解碼
            byte[] encodedKey = Base64Utils.decodeFromString(publicKey);
            // 生成公鑰
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            // 簽名摘要算法
            Signature signature = Signature.getInstance("SHA256WithRSA");
            // 用公鑰初始化簽名
            signature.initVerify(pubKey);
            // 使用指定的字節(jié)數組更新簽名或驗證
            signature.update(content);
            // base64解碼后進行驗證
            return signature.verify(Base64Utils.decodeFromString(sign));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static void main(String[] args) throws Exception {
        //客戶端代碼
        String text = "hello";
        //使用服務端公鑰加密
        byte[] encryptText = RSACipher.encrypt(Config.SERVER_PUBLIC_KEY, text.getBytes());
        System.out.println("加密后:\n" + new String(encryptText));
        //使用客戶端私鑰簽名
        String signature = RSACipher.sign(Config.CLIENT_PRIVATE_KEY, encryptText);
        System.out.println("簽名:\n" + signature);
        //服務端代碼
        //使用客戶端公鑰驗簽
        boolean result = RSACipher.checkSign(Config.CLIENT_PUBLIC_KEY, encryptText, signature);
        System.out.println("驗簽:\n" + result);
        //使用服務端私鑰解密
        byte[] decryptText = RSACipher.decrypt(Config.SERVER_PRIVATE_KEY, encryptText);
        System.out.println("解密后:\n" + new String(decryptText));
    }
}

輸出結果

加密后:
ODdEkwo1RgRW8UMoHXPKe9Gwcp6lTCkg4P/Ra3gfkrO+Fw6pSgo0H54nMC5sYSsoUVy1wy2/QXeLSwR6Obfl7SU7DeW+XdGee83O2kgdsDQPbYFwlPYTd0cdOmWwZxtgEOIB9d5G75Iut4kci15vrhXZVtku92U+7aNwtYimSDQ=
簽名:
RL1qIScizRyu79/y+r2TN2FL/bSQDxnDj4JlDwSZM6XZR7CL7u5ZjLNHbsSYpHaCv9qKMS4ump50LyF+go05dsPjWZOvFNkgcm9LepkDP1qm8AzKdTGwlzhdBmy2397Ed8uBrQocFGj/721Y2xM/Db0nt7r54zKZkDXbMMlsd9k=
驗簽:
true
解密后:
hello

3、AES對稱加密

import org.springframework.util.Base64Utils;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

public class AESCipher {

    public static SecureRandom random = new SecureRandom();

    /**
     * 獲取隨機16位key,key必須要是10的整數倍,否則會出錯
    */
    public static String getRandom(int length) {
        StringBuilder ret = new StringBuilder();
        for (int i = 0; i < length; i++) {
            // 輸出字母還是數字
            boolean isChar = (random.nextInt(2) % 2 == 0);
            // 字符串
            if (isChar) {
                // 取得大寫字母還是小寫字母
                int choice = random.nextInt(2) % 2 == 0 ? 65 : 97;
                ret.append((char) (choice + random.nextInt(26)));
            } else { // 數字
                ret.append(random.nextInt(10));
            }
        }
        return ret.toString();
    }

    /**
     * 加密方法,使用key充當向量iv,增加加密算法的強度
     * 更加安全
     * @param key 密鑰
     * @param raw 需要加密的內容
     * @return
     */
    public static String encrypt(byte[] key, String raw) throws Exception {
        // 第一次加密
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        byte[] enCodeFormat = secretKey.getEncoded();
        // 獲取二次加密的key
        SecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");
        Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);
        // 向量iv,增加加密算法的強度
        IvParameterSpec iv = new IvParameterSpec(key);
        // 初始化加密器
        cipher.init(Cipher.ENCRYPT_MODE, secondSecretKey, iv);
        // 加密
        byte[] result = cipher.doFinal(raw.getBytes());
        // 進行base64編碼
        return Base64Utils.encodeToString(result);
    }

    /**
     * 解密方法,使用key充當向量iv,增加加密算法的強度
     * @param key 密鑰
     * @param enc 待解密內容
     * @return
     */
    public static String decrypt(byte[] key, String enc) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        byte[] enCodeFormat = secretKey.getEncoded();
        // 二次加密
        SecretKeySpec secondSecretKey = new SecretKeySpec(enCodeFormat, "AES");

        Cipher cipher = Cipher.getInstance(Config.AES_ALGORITHM);
        IvParameterSpec iv = new IvParameterSpec(key);
        // 初始化
        cipher.init(Cipher.DECRYPT_MODE, secondSecretKey, iv);
        // 首先進行base64解碼
        byte[] bytes = Base64Utils.decodeFromString(enc);
        // 解密
        byte[] result = cipher.doFinal(bytes);
        return new String(result);
    }

    public static void main(String[] args) throws Exception {

        //客戶端代碼
        String text = "hello";
        //隨機生成16位aes密鑰,也可以自己指定16位
        byte[] aesKey = getRandom(16).getBytes();

        String encryptText = AESCipher.encrypt(aesKey, text);
        System.out.println("加密后:\n" + encryptText);
        String decryptText = AESCipher.decrypt(aesKey, encryptText);
        System.out.println("解密后:\n" + decryptText);
    }
}

輸出結果

加密后:
hwkYAF9eXj/dytmDBD30xg==
解密后:
hello

三、加解密 starter實戰(zhàn)

1、介紹

加密解密本身并不是難事,問題是在何時去處理?定義一個過濾器,將請求和響應分別攔截下來進行處理也是一個辦法,這種方式雖然粗暴,但是靈活,因為可以拿到一手的請求參數和響應數據。不過 SpringMVC 中給我們提供了 ResponseBodyAdvice和 RequestBodyAdvice,利用這兩個工具可以對請求和響應進行預處理,非常方便。

參考:

2、前期準備

2.1 引入依賴

因為我們這個工具是為 Web 項目開發(fā)的,以后必然使用在 Web 環(huán)境中,所以這里添加依賴時 scope 設置為 provided

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>provided</scope>
    <version>2.7.0</version>
</dependency>

scope幾個屬性介紹

  • compile:默認值 他表示被依賴項目需要參與當前項目的編譯,還有后續(xù)的測試,運行周期也參與其中,是一個比較強的依賴。打包的時候通常需要包含進去
  • test:依賴項目僅僅參與測試相關的工作,包括測試代碼的編譯和執(zhí)行,不會被打包,例如:junit
  • runtime:表示被依賴項目無需參與項目的編譯,不過后期的測試和運行周期需要其參與。與compile相比,跳過了編譯而已。例如JDBC驅動,適用運行和測試階段
  • provided:打包的時候可以不用包進去,別的設施會提供。事實上該依賴理論上可以參與編譯,測試,運行等周期。相當于compile,但是打包階段做了exclude操作
  • system:從參與度來說,和provided相同,不過被依賴項不會從maven倉庫下載,而是從本地文件系統(tǒng)拿。需要添加systemPath的屬性來定義路徑

2.2 封裝公共相應類

public class RespBean {
    private Integer status;
    private String msg;
    private Object obj;

    public static RespBean build() {
        return new RespBean();
    }

    public static RespBean ok(String msg) {
        return new RespBean(200, msg, null);
    }

    public static RespBean ok(String msg, Object obj) {
        return new RespBean(200, msg, obj);
    }

    public static RespBean error(String msg) {
        return new RespBean(500, msg, null);
    }

    public static RespBean error(String msg, Object obj) {
        return new RespBean(500, msg, obj);
    }

    private RespBean() {
    }

    private RespBean(Integer status, String msg, Object obj) {
        this.status = status;
        this.msg = msg;
        this.obj = obj;
    }

    public Integer getStatus() {
        return status;
    }

    public RespBean setStatus(Integer status) {
        this.status = status;
        return this;
    }

    public String getMsg() {
        return msg;
    }

    public RespBean setMsg(String msg) {
        this.msg = msg;
        return this;
    }

    public Object getObj() {
        return obj;
    }

    public RespBean setObj(Object obj) {
        this.obj = obj;
        return this;
    }
}

2.3 定義加解密工具類

加密這塊有多種方案可以選擇,對稱加密、非對稱加密,其中對稱加密又可以使用 AES、DES、3DES 等不同算法,這里我們使用 Java 自帶的 Cipher 來實現對稱加密,使用 AES 算法

public class AESUtils {

    private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";

    // 獲取 cipher
    private static Cipher getCipher(byte[] key, int model) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(model, secretKeySpec);
        return cipher;
    }

    // AES加密
    public static String encrypt(byte[] data, byte[] key) throws Exception {
        Cipher cipher = getCipher(key, Cipher.ENCRYPT_MODE);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data));
    }

    // AES解密
    public static byte[] decrypt(byte[] data, byte[] key) throws Exception {
        Cipher cipher = getCipher(key, Cipher.DECRYPT_MODE);
        return cipher.doFinal(Base64.getDecoder().decode(data));
    }
}

2.4 定義兩個注解

接下來我們定義兩個注解 @Decrypt 和 @Encrypt。在以后使用的過程中,哪個接口方法添加了 @Encrypt 注解就對哪個接口的數據加密返回,哪個接口/參數添加了 @Decrypt 注解就對哪個接口/參數進行解密。另外就是 @Decrypt 可以用在參數上

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.PARAMETER})
public @interface Decrypt {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Encrypt {
}

2.5 設置自定義key

定義一個 EncryptProperties 類來讀取用戶配置的 key,這樣就可以自定義key。這里設置了默認值,以后如果用戶想自己配置 key,只需要在 application.properties 中配置 spring.encrypt.key=xxx 即可。

@ConfigurationProperties(prefix = "spring.encrypt")
@Component
public class EncryptProperties {
    // 這一塊一定要16位或者整數倍,最多256
    private final static String DEFAULT_KEY = "www.shawn222.com";
    private String key = DEFAULT_KEY;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }
}

3、接口加密與解密

3.1 介紹

ResponseBodyAdvice 在你使用了 @ResponseBody 注解的時候才會生效,RequestBodyAdvice 在你使用了 @RequestBody 注解的時候才會生效,換言之,前后端都是 JSON 交互的時候,這兩個才有用

3.2 接口加密

我們自定義 EncryptResponse 類實現 ResponseBodyAdvice接口,泛型表示接口的返回類型,這里一共要實現兩個方法

  • supports:這個方法用來判斷什么樣的接口需要加密,參數 returnType 表示返回類型,我們這里的判斷邏輯就是方法是否含有 @Encrypt 注解,如果有,表示該接口需要加密處理,如果沒有,表示該接口不需要加密處理。
  • beforeBodyWrite:這個方法會在數據響應之前執(zhí)行,也就是我們先對響應數據進行二次處理,處理完成后,才會轉成 json 返回。我們這里的處理方式很簡單,RespBean 中的 status 是狀態(tài)碼就不用加密了,另外兩個字段重新加密后重新設置值即可。

另外需要注意,自定義的 ResponseBodyAdvice 需要用 @ControllerAdvice 注解來標記。

@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class EncryptResponse implements ResponseBodyAdvice<RespBean> {
    
    private ObjectMapper om = new ObjectMapper();
    
    @Autowired
    EncryptProperties encryptProperties;
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        return returnType.hasMethodAnnotation(Encrypt.class);
    }


    @Override
    public RespBean beforeBodyWrite(RespBean body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        byte[] keyBytes = encryptProperties.getKey().getBytes();
        try {
            if (body.getMsg()!=null) {
                body.setMsg(AESUtils.encrypt(body.getMsg().getBytes(),keyBytes));
            }
            if (body.getObj() != null) {
                body.setObj(AESUtils.encrypt(om.writeValueAsBytes(body.getObj()), keyBytes));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return body;
    }
}

3.3 接口解密

首先大家注意,DecryptRequest 類我們沒有直接實現 RequestBodyAdvice 接口,而是繼承自 RequestBodyAdviceAdapter 類,該類是 RequestBodyAdvice 接口的子類,并且實現了接口中的一些方法,這樣當我們繼承自 RequestBodyAdviceAdapter 時,就只需要根據自己實際需求實現某幾個方法即可。

  • supports:該方法用來判斷哪些接口需要處理接口解密,我們這里的判斷邏輯是方法上或者參數上含有 @Decrypt 注解的接口,處理解密問題。
  • beforeBodyRead:這個方法會在參數轉換成具體的對象之前執(zhí)行,我們先從流中加載到數據,然后對數據進行解密,解密完成后再重新構造 HttpInputMessage 對象返回。
@EnableConfigurationProperties(EncryptProperties.class)
@ControllerAdvice
public class DecryptRequest extends RequestBodyAdviceAdapter {
    
    @Autowired
    EncryptProperties encryptProperties;
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; converterType) {
        return methodParameter.hasMethodAnnotation(Decrypt.class) || methodParameter.hasParameterAnnotation(Decrypt.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(final HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class&lt;? extends HttpMessageConverter&lt;?&gt;&gt; converterType) throws IOException {
        byte[] body = new byte[inputMessage.getBody().available()];
        inputMessage.getBody().read(body);
        try {
            byte[] decrypt = AESUtils.decrypt(body, encryptProperties.getKey().getBytes());
            final ByteArrayInputStream bais = new ByteArrayInputStream(decrypt);
            return new HttpInputMessage() {
                @Override
                public InputStream getBody() throws IOException {
                    return bais;
                }

                @Override
                public HttpHeaders getHeaders() {
                    return inputMessage.getHeaders();
                }
            };
        } catch (Exception e) {
            e.printStackTrace();
        }
        return super.beforeBodyRead(inputMessage, parameter, targetType, converterType);
    }
}

4、打包發(fā)布starter

4.1 定義自動化配置類

// 換成自己的包路徑
@Configuration
@ComponentScan("com.example.encryption")
public class EncryptAutoConfiguration {
}

最后,resources 目錄下定義 META-INF,然后再定義 spring.factories 文件,這樣當項目啟動時,就會自動加載該配置類

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.encryption.EncryptAutoConfiguration

安裝到本地倉庫比較簡單,直接 mvn install,或者在 IDEA 中,點擊右邊的 Maven,然后雙擊 install

4.2 發(fā)布線上使用

發(fā)不到線上我們可以使用 JitPack來做。首先我們在 GitHub 上創(chuàng)建一個倉庫,將我們的代碼上傳上去,上傳成功后,點擊右邊的 Create a new release 按鈕,發(fā)布一個正式版

發(fā)布成功后,打開 jitpack,輸入倉庫的完整路徑,點擊 lookup 按鈕,查找到之后,再點擊 Get it 按鈕完成構建,構建成功后,JitPack 上會給出項目引用方式,新建項目時引入即可

5、新項目使用

創(chuàng)建實體類

public class User {
    private Long id;
    private String username;
    //省略 getter/setter
}

創(chuàng)建測試類,第一個接口使用了 @Encrypt 注解,所以會對該接口的數據進行加密(如果不使用該注解就不加密),第二個接口使用了 @Decrypt 所以會對上傳的參數進行解密,注意 @Decrypt 注解既可以放在方法上也可以放在參數上。

@RestController
public class HelloController {

    @GetMapping("/user")
    @Encrypt
    public RespBean getUser() {
        User user = new User();
        user.setId((long) 99);
        user.setUsername("javaboy");
        return RespBean.ok("ok", user);
    }

    @PostMapping("/user")
    public RespBean addUser(@RequestBody @Decrypt User user) {
        System.out.println("user = " + user);
        return RespBean.ok("ok", user);
    }
}

參考文章

如何優(yōu)雅的實現 SpringBoot 接口參數加密解密?

為什么使用 Java Cipher 要指定轉換模式?

Hutool加密解密

【網絡】java密碼安全

到此這篇關于SpringBoot接口加密與解密的實現的文章就介紹到這了,更多相關SpringBoot接口加密與解密內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java程序執(zhí)行cmd命令全過程

    Java程序執(zhí)行cmd命令全過程

    這篇文章主要介紹了Java程序執(zhí)行cmd命令全過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Java虛擬機的核心原理與工作機制解讀

    Java虛擬機的核心原理與工作機制解讀

    這篇文章主要介紹了Java虛擬機的核心原理與工作機制,將深入探討JVM的核心概念、架構、內存模型、垃圾回收機制等內容,幫助你全面掌握JVM的知識
    2025-05-05
  • Java并發(fā)工具類之CountDownLatch詳解

    Java并發(fā)工具類之CountDownLatch詳解

    這篇文章主要介紹了Java并發(fā)工具類之CountDownLatch詳解,CountDownLatch可以使一個獲多個線程等待其他線程各自執(zhí)行完畢后再執(zhí)行,CountDownLatch可以解決那些一個或者多個線程在執(zhí)行之前必須依賴于某些必要的前提業(yè)務先執(zhí)行的場景,需要的朋友可以參考下
    2023-12-12
  • Java分析講解序列化與字典功能的序列化

    Java分析講解序列化與字典功能的序列化

    在項目開發(fā)地過程中經常會用到字典存儲某些可配置的值,但相應的也會給開發(fā)帶來復雜度,比如數據庫存儲的值為字典值:0,1,2,3這種類型的值,但是前端頁面卻需要展示為 啟動、禁用、刪除 等中文狀態(tài),下面我們來看兩種解決方案
    2022-06-06
  • SpringBoot使用@Slf4j注解實現日志輸出的示例代碼

    SpringBoot使用@Slf4j注解實現日志輸出的示例代碼

    @Slf4j 是 Lombok 庫中的一個注解,它極大地簡化了日志記錄的代碼,通過使用這個注解,Lombok 會自動在你的類中注入一個靜態(tài)的日志對象,本文給大家介紹了SpringBoot使用@Slf4j注解實現日志輸出的方法,需要的朋友可以參考下
    2024-10-10
  • SpringBoot中的Thymeleaf用法

    SpringBoot中的Thymeleaf用法

    Thymeleaf是最近SpringBoot推薦支持的模板框架。本文重點給大家介紹SpringBoot中的Thymeleaf用法,需要的的朋友參考下吧
    2017-05-05
  • SpringBoot簡單使用SpringData的jdbc和durid

    SpringBoot簡單使用SpringData的jdbc和durid

    今天給大家?guī)淼氖顷P于Java的相關知識,文章圍繞著SpringBoot簡單使用SpringData的jdbc和durid,文中有非常詳細的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • Shiro的運行大致流程詳解

    Shiro的運行大致流程詳解

    這篇文章主要介紹了Shiro的運行大致流程詳解,Shiro和SpringSecurity都是Java領域中常用的安全框架,它們都提供了身份認證和授權功能,可以幫助開發(fā)者快速構建安全的應用程序,需要的朋友可以參考下
    2023-07-07
  • JavaWeb實現簡單的自動登錄功能

    JavaWeb實現簡單的自動登錄功能

    這篇文章主要為大家詳細介紹了JavaWeb實現簡單的自動登錄功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 使用Jackson反序列化遇到的問題及解決

    使用Jackson反序列化遇到的問題及解決

    這篇文章主要介紹了使用Jackson反序列化遇到的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08

最新評論