SpringBoot實(shí)現(xiàn)RSA+AES自動(dòng)接口解密的實(shí)戰(zhàn)指南
在當(dāng)今數(shù)據(jù)泄露頻發(fā)的網(wǎng)絡(luò)環(huán)境中,接口安全已成為開(kāi)發(fā)者不可忽視的核心議題。RSA+AES混合加密方案因其安全性高、性能優(yōu)越而被廣泛采用:
- RSA(非對(duì)稱(chēng)加密):解決密鑰分發(fā)難題,適合加密小數(shù)據(jù)(如AES密鑰)。
- AES(對(duì)稱(chēng)加密):高效加密大量數(shù)據(jù),適合傳輸業(yè)務(wù)參數(shù)。
本文將深入SpringBoot框架,手把手演示如何通過(guò)自定義注解+攔截器實(shí)現(xiàn)接口的自動(dòng)化加解密,并提供完整工具類(lèi)、AOP切片代碼及安全優(yōu)化策略。
一、項(xiàng)目依賴(lài)與環(huán)境準(zhǔn)備
1.1 Maven依賴(lài)配置
在pom.xml中添加必要的依賴(lài):
<dependencies>
<!-- SpringBoot Web支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSON處理工具 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- 加密算法擴(kuò)展(支持BCrypt等) -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
<!-- Lombok簡(jiǎn)化代碼 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
1.2 密鑰生成與配置
RSA密鑰對(duì)生成:
// 工具類(lèi):生成RSA密鑰對(duì)
public class KeyGenerator {
public static void main(String[] args) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048); // 2048位密鑰長(zhǎng)度
KeyPair keyPair = keyGen.generateKeyPair();
// 公鑰(客戶(hù)端使用)
String publicKeyStr = Base64.getEncoder().encodeToString(keyPair.getPublic().getEncoded());
System.out.println("Public Key:\n" + publicKeyStr);
// 私鑰(服務(wù)端使用)
String privateKeyStr = Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded());
System.out.println("Private Key:\n" + privateKeyStr);
}
}
配置文件中存儲(chǔ)密鑰:
# application.yml
encrypt:
rsa:
public-key: "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
private-key: "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC..."
二、加密工具類(lèi)實(shí)現(xiàn)
2.1 RSA工具類(lèi)
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
public class RSAUtil {
static {
Security.addProvider(new BouncyCastleProvider()); // 添加BC支持
}
// 使用公鑰加密
public static String encrypt(String data, String publicKeyStr) throws Exception {
PublicKey publicKey = getPublicKeyFromBase64(publicKeyStr);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));
}
// 使用私鑰解密
public static String decrypt(String encryptedData, String privateKeyStr) throws Exception {
PrivateKey privateKey = getPrivateKeyFromBase64(privateKeyStr);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(encryptedData)));
}
// 從Base64字符串生成公鑰
private static PublicKey getPublicKeyFromBase64(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePublic(spec);
}
// 從Base64字符串生成私鑰
private static PrivateKey getPrivateKeyFromBase64(String keyStr) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(keyStr);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
return keyFactory.generatePrivate(spec);
}
}
2.2 AES工具類(lèi)
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
public class AESUtil {
// AES加密模式:CBC + PKCS7Padding
private static final String AES_MODE = "AES/CBC/PKCS7Padding";
private static final int IV_LENGTH = 16; // 初始化向量長(zhǎng)度
// 生成隨機(jī)AES密鑰
public static String generateAESKey() throws Exception {
byte[] key = new byte[16]; // 128位密鑰
new SecureRandom().nextBytes(key);
return Base64.getEncoder().encodeToString(key);
}
// AES加密
public static String encrypt(String data, String aesKey) throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(aesKey);
SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES");
byte[] iv = new byte[IV_LENGTH];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(AES_MODE);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
// 將IV和密文合并返回
byte[] result = new byte[iv.length + encryptedBytes.length];
System.arraycopy(iv, 0, result, 0, iv.length);
System.arraycopy(encryptedBytes, 0, result, iv.length, encryptedBytes.length);
return Base64.getEncoder().encodeToString(result);
}
// AES解密
public static String decrypt(String encryptedData, String aesKey) throws Exception {
byte[] data = Base64.getDecoder().decode(encryptedData);
byte[] iv = new byte[IV_LENGTH];
byte[] encryptedBytes = new byte[data.length - IV_LENGTH];
System.arraycopy(data, 0, iv, 0, IV_LENGTH);
System.arraycopy(data, IV_LENGTH, encryptedBytes, 0, encryptedBytes.length);
SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(aesKey), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(AES_MODE);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
return new String(cipher.doFinal(encryptedBytes), StandardCharsets.UTF_8);
}
}
三、自定義注解與AOP切面
3.1 自定義解密注解 @RequestRSA
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestRSA {
// 標(biāo)記需要解密的接口
}
3.2 AOP切面實(shí)現(xiàn)自動(dòng)解密
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* RSA+AES接口解密切面
*/
@Aspect
@Component
@Order(1)
public class RequestRSAAspect {
@Value("${encrypt.rsa.private-key}")
private String privateKey;
/**
* 攔截帶有@RequestRSA注解的方法
*/
@Around("@annotation(requestRSA) || @within(requestRSA)")
public Object decryptRequest(ProceedingJoinPoint joinPoint, RequestRSA requestRSA) throws Throwable {
// 獲取請(qǐng)求體
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String body = getRequestBody(request);
// 解析加密參數(shù)
JSONObject encryptedJson = JSONObject.parseObject(body);
String sym = encryptedJson.getString("sym"); // RSA加密的AES密鑰
String asy = encryptedJson.getString("asy"); // AES加密的業(yè)務(wù)數(shù)據(jù)
// RSA解密獲取AES密鑰
String aesKey = RSAUtil.decrypt(sym, privateKey);
// AES解密業(yè)務(wù)數(shù)據(jù)
String decryptedData = AESUtil.decrypt(asy, aesKey);
// 將解密后的數(shù)據(jù)注入方法參數(shù)
Object[] args = injectDecryptedData(joinPoint, decryptedData);
return joinPoint.proceed(args);
}
/**
* 讀取請(qǐng)求體內(nèi)容
*/
private String getRequestBody(HttpServletRequest request) throws Exception {
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
return sb.toString();
}
/**
* 將解密后的數(shù)據(jù)注入方法參數(shù)
*/
private Object[] injectDecryptedData(ProceedingJoinPoint joinPoint, String decryptedData) throws Exception {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Parameter[] parameters = method.getParameters();
Object[] args = joinPoint.getArgs();
for (int i = 0; i < parameters.length; i++) {
if (parameters[i].isAnnotationPresent(RequestBody.class)) {
// 將解密后的JSON字符串反序列化為目標(biāo)對(duì)象
args[i] = JSONObject.parseObject(decryptedData, parameters[i].getParameterizedType());
}
}
return args;
}
}
四、客戶(hù)端加密邏輯實(shí)現(xiàn)
4.1 客戶(hù)端加密流程
public class ClientEncryption {
public static void main(String[] args) throws Exception {
// 1. 隨機(jī)生成AES密鑰和IV
String aesKey = AESUtil.generateAESKey();
String aesIV = AESUtil.generateAESKey(); // 實(shí)際應(yīng)使用SecureRandom生成IV
// 2. 構(gòu)造請(qǐng)求參數(shù)
JSONObject requestData = new JSONObject();
requestData.put("username", "user123");
requestData.put("password", "securePass");
// 3. AES加密業(yè)務(wù)數(shù)據(jù)
String asy = AESUtil.encrypt(requestData.toJSONString(), aesKey);
// 4. 構(gòu)造AES密鑰信息
JSONObject aesInfo = new JSONObject();
aesInfo.put("key", aesKey);
aesInfo.put("keyVI", aesIV);
aesInfo.put("time", System.currentTimeMillis());
// 5. RSA加密AES密鑰信息
String sym = RSAUtil.encrypt(aesInfo.toJSONString(), "服務(wù)器公鑰");
// 6. 構(gòu)造最終請(qǐng)求體
JSONObject finalRequest = new JSONObject();
finalRequest.put("sym", sym);
finalRequest.put("asy", asy);
System.out.println("加密后請(qǐng)求體:\n" + finalRequest.toJSONString());
}
}
五、服務(wù)器端接口示例
5.1 帶解密注解的接口
@RestController
@RequestMapping("/api")
public class SecureController {
@PostMapping("/login")
@RequestRSA // 標(biāo)記需要解密
public Response login(@RequestBody LoginRequest request) {
// 直接使用解密后的參數(shù)
return new Response("登錄成功", request.getUsername());
}
}
// 請(qǐng)求參數(shù)類(lèi)
@Data
public class LoginRequest {
private String username;
private String password;
}
六、安全優(yōu)化與最佳實(shí)踐
6.1 密鑰管理策略
- 私鑰存儲(chǔ):使用配置中心(如Nacos)或密鑰管理服務(wù)(如AWS KMS)。
- 公鑰分發(fā):通過(guò)HTTPS協(xié)議分發(fā),避免中間人攻擊。
- 定期輪換:每30天更新一次RSA密鑰對(duì)。
6.2 時(shí)間戳防重放攻擊
在AOP切面中添加時(shí)間戳校驗(yàn):
private void validateTimestamp(JSONObject aesInfo) {
long requestTime = aesInfo.getLong("time");
long currentTime = System.currentTimeMillis();
if (Math.abs(currentTime - requestTime) > 60000) { // 允許1分鐘內(nèi)
throw new RuntimeException("請(qǐng)求超時(shí),請(qǐng)重試");
}
}
6.3 性能優(yōu)化建議
- 緩存公鑰/私鑰對(duì)象:避免重復(fù)解析Base64字符串。
- 異步解密:對(duì)高并發(fā)接口采用線(xiàn)程池異步處理。
- 壓縮數(shù)據(jù):對(duì)加密后的數(shù)據(jù)進(jìn)行GZIP壓縮減少傳輸體積。
七、完整調(diào)用流程圖解
客戶(hù)端流程: [生成AES密鑰] → [AES加密數(shù)據(jù)] → [構(gòu)造AES信息] → [RSA加密AES信息] → [發(fā)送請(qǐng)求] 服務(wù)端流程: [接收請(qǐng)求] → [AOP切面攔截] → [RSA解密AES密鑰] → [AES解密業(yè)務(wù)數(shù)據(jù)] → [注入?yún)?shù)執(zhí)行接口]
八、 構(gòu)建高安全接口的核心要點(diǎn)
| 模塊 | 關(guān)鍵點(diǎn) | 注意事項(xiàng) |
|---|---|---|
| 密鑰管理 | RSA公私鑰分離存儲(chǔ),AES密鑰動(dòng)態(tài)生成 | 私鑰需加密存儲(chǔ),定期輪換 |
| 加密流程 | RSA加密AES密鑰,AES加密業(yè)務(wù)數(shù)據(jù) | 確保IV隨機(jī)性,避免重復(fù)使用 |
| AOP切面設(shè)計(jì) | 自動(dòng)攔截、解密、參數(shù)注入 | 處理異常情況(如密鑰錯(cuò)誤、時(shí)間戳過(guò)期) |
| 安全防護(hù) | 時(shí)間戳防重放、HTTPS傳輸、數(shù)據(jù)完整性校驗(yàn) | 避免硬編碼密鑰,使用配置中心 |
九、 工具類(lèi)與依賴(lài)說(shuō)明
- Bouncy Castle:提供擴(kuò)展的加密算法支持(如PKCS7Padding)。
- FastJSON:高效JSON序列化/反序列化。
- Lombok:簡(jiǎn)化POJO類(lèi)的編寫(xiě)(如
@Data注解)。
以上就是SpringBoot實(shí)現(xiàn)RSA+AES自動(dòng)接口解密的實(shí)戰(zhàn)指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot RSA AES接口解密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mybatisplus開(kāi)啟sql打印的三種方式匯總
這篇文章主要介紹了mybatisplus開(kāi)啟sql打印的三種方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2024-01-01
Java事務(wù)管理學(xué)習(xí)之JDBC詳解
這篇文章主要介紹了Java事務(wù)管理學(xué)習(xí)之JDBC的相關(guān)資料,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-03-03
基于Java 數(shù)組內(nèi)存分配的相關(guān)問(wèn)題
本篇文章是對(duì)Java中數(shù)組內(nèi)存分配進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
Java面向?qū)ο髮?shí)現(xiàn)汽車(chē)租賃系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java面向?qū)ο髮?shí)現(xiàn)汽車(chē)租賃系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
使用mybatis-plus分頁(yè)出現(xiàn)兩個(gè)Limit的問(wèn)題解決
在使用MyBatis-Plus進(jìn)行分頁(yè)查詢(xún)時(shí),可能會(huì)遇到查詢(xún)SQL中出現(xiàn)兩個(gè)limit語(yǔ)句的問(wèn)題,這通常是由于在多個(gè)模塊中重復(fù)引入了MyBatis-Plus的分頁(yè)插件所導(dǎo)致的,下面就來(lái)介紹一下如何解決,感興趣的可以了解一下2024-10-10
JAVA覆蓋和重寫(xiě)的區(qū)別及說(shuō)明
非靜態(tài)方法的覆蓋即重寫(xiě),具有多態(tài)性;靜態(tài)方法無(wú)法被覆蓋,但可被重寫(xiě)(僅通過(guò)類(lèi)名調(diào)用),二者區(qū)別在于綁定時(shí)機(jī)與引用類(lèi)型關(guān)聯(lián)性2025-07-07

