推荐方案:Interceptor(拦截器)
主要优势:
- 请求生命周期更早:在进入Controller之前进行校验,避免不必要的业务逻辑执行
- 统一处理:可以统一处理所有需要签名验证的接口
- 性能优化:在请求处理链的早期拦截非法请求,减少资源消耗
- 配置灵活:可以通过配置指定哪些路径需要签名校验
package com.example.crypto;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;
/**
* AES加密解密工具类
* 支持多种加密模式:GCM、CBC、ECB
*/
public class AESCrypto {
private static final String ALGORITHM = "AES";
private static final int KEY_SIZE = 256;
// 加密模式常量
public static final String MODE_GCM = "AES/GCM/NoPadding";
public static final String MODE_CBC = "AES/CBC/PKCS5Padding";
public static final String MODE_ECB = "AES/ECB/PKCS5Padding";
// GCM参数
private static final int GCM_TAG_LENGTH = 128;
private static final int GCM_IV_LENGTH = 12;
// CBC参数
private static final int CBC_IV_LENGTH = 16;
/**
* 生成AES密钥
*/
public static SecretKey generateKey() throws Exception {
KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
keyGenerator.init(KEY_SIZE);
return keyGenerator.generateKey();
}
/**
* 从Base64字符串恢复密钥
*/
public static SecretKey restoreKey(String base64Key) {
byte[] keyBytes = Base64.getDecoder().decode(base64Key);
return new SecretKeySpec(keyBytes, ALGORITHM);
}
/**
* AES加密 - GCM模式(推荐)
*/
public static String aesEncryptGCM(String plaintext, SecretKey key) throws Exception {
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance(MODE_GCM);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// 组合IV和密文
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* AES解密 - GCM模式
*/
public static String aesDecryptGCM(String encryptedText, SecretKey key) throws Exception {
byte[] decoded = Base64.getDecoder().decode(encryptedText);
// 分离IV和密文
byte[] iv = new byte[GCM_IV_LENGTH];
byte[] ciphertext = new byte[decoded.length - GCM_IV_LENGTH];
System.arraycopy(decoded, 0, iv, 0, iv.length);
System.arraycopy(decoded, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance(MODE_GCM);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, StandardCharsets.UTF_8);
}
/**
* AES加密 - CBC模式
*/
public static String aesEncryptCBC(String plaintext, SecretKey key) throws Exception {
byte[] iv = new byte[CBC_IV_LENGTH];
new SecureRandom().nextBytes(iv);
Cipher cipher = Cipher.getInstance(MODE_CBC);
IvParameterSpec spec = new IvParameterSpec(iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
// 组合IV和密文
byte[] encrypted = new byte[iv.length + ciphertext.length];
System.arraycopy(iv, 0, encrypted, 0, iv.length);
System.arraycopy(ciphertext, 0, encrypted, iv.length, ciphertext.length);
return Base64.getEncoder().encodeToString(encrypted);
}
/**
* AES解密 - CBC模式
*/
public static String aesDecryptCBC(String encryptedText, SecretKey key) throws Exception {
byte[] decoded = Base64.getDecoder().decode(encryptedText);
// 分离IV和密文
byte[] iv = new byte[CBC_IV_LENGTH];
byte[] ciphertext = new byte[decoded.length - CBC_IV_LENGTH];
System.arraycopy(decoded, 0, iv, 0, iv.length);
System.arraycopy(decoded, iv.length, ciphertext, 0, ciphertext.length);
Cipher cipher = Cipher.getInstance(MODE_CBC);
IvParameterSpec spec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] plaintext = cipher.doFinal(ciphertext);
return new String(plaintext, StandardCharsets.UTF_8);
}
/**
* 通用AES加密方法
*/
public static String aesEncrypt(String plaintext, String base64Key, String mode) throws Exception {
SecretKey key = restoreKey(base64Key);
switch (mode.toUpperCase()) {
case "GCM":
return aesEncryptGCM(plaintext, key);
case "CBC":
return aesEncryptCBC(plaintext, key);
default:
throw new IllegalArgumentException("不支持的加密模式: " + mode);
}
}
/**
* 通用AES解密方法
*/
public static String aesDecrypt(String encryptedText, String base64Key, String mode) throws Exception {
SecretKey key = restoreKey(base64Key);
switch (mode.toUpperCase()) {
case "GCM":
return aesDecryptGCM(encryptedText, key);
case "CBC":
return aesDecryptCBC(encryptedText, key);
default:
throw new IllegalArgumentException("不支持的加密模式: " + mode);
}
}
/**
* 验证加密数据格式
*/
public static boolean isValidEncryptedData(String encryptedText, String mode) {
try {
byte[] decoded = Base64.getDecoder().decode(encryptedText);
switch (mode.toUpperCase()) {
case "GCM":
return decoded.length >= GCM_IV_LENGTH;
case "CBC":
return decoded.length >= CBC_IV_LENGTH;
default:
return false;
}
} catch (Exception e) {
return false;
}
}
}
<code_end>
<code_start project_name=java_aes_crypto filename=src/main/java/com/example/crypto/AESKeyManager.java title=AES密钥管理器 entrypoint=false runnable=false project_final_file=false>
package com.example.crypto;
import javax.crypto.SecretKey;
import java.util.HashMap;
import java.util.Map;
/**
* AES密钥管理器
* 提供密钥的生成、存储和管理功能
*/
public class AESKeyManager {
private static final Map<String, SecretKey> keyStore = new HashMap<>();
/**
* 生成并存储新密钥
*/
public static String generateAndStoreKey(String keyId) throws Exception {
SecretKey key = AESCrypto.generateKey();
String base64Key = Base64.getEncoder().encodeToString(key.getEncoded());
keyStore.put(keyId, key);
return base64Key;
}
/**
* 根据keyId获取密钥
*/
public static SecretKey getKey(String keyId) {
return keyStore.get(keyId);
}
/**
* 删除密钥
*/
public static boolean removeKey(String keyId) {
return keyStore.remove(keyId) != null;
}
/**
* 导出所有密钥
*/
public static Map<String, String> exportAllKeys() {
Map<String, String> exportedKeys = new HashMap<>();
for (Map.Entry<String, SecretKey> entry : keyStore.entrySet()) {
String base64Key = Base64.getEncoder().encodeToString(entry.getValue().getEncoded());
exportedKeys.put(entry.getKey(), base64Key);
}
return exportedKeys;
}
/**
* 导入密钥
*/
public static void importKey(String keyId, String base64Key) {
SecretKey key = AESCrypto.restoreKey(base64Key);
keyStore.put(keyId, key);
}
}
<code_end>
<code_start project_name=java_aes_crypto filename=src/main/java/com/example/demo/AESDemo.java title=AES加密解密演示 entrypoint=true runnable=true project_final_file=false>
package com.example.demo;
import com.example.crypto.AESCrypto;
import com.example.crypto.AESKeyManager;
import javax.crypto.SecretKey;
import java.util.Base64;
/**
* AES加密解密演示类
*/
public class AESDemo {
public static void main(String[] args) {
try {
System.out.println("=== AES加密解密演示 ===\n");
// 1. 生成密钥
System.out.println("1. 生成AES-256密钥...");
SecretKey key = AESCrypto.generateKey();
String base64Key = Base64.getEncoder().encodeToString(key.getEncoded());
System.out.println("密钥(Base64): " + base64Key);
System.out.println();
// 2. GCM模式加密解密
System.out.println("2. GCM模式加密解密测试...");
String originalText = "这是一个需要加密的敏感数据,包含中文和English混合内容!");
System.out.println("原始文本: " + originalText);
String encryptedGCM = AESCrypto.aesEncryptGCM(originalText, key);
System.out.println("GCM加密结果: " + encryptedGCM);
String decryptedGCM = AESCrypto.aesDecryptGCM(encryptedGCM, key);
System.out.println("GCM解密结果: " + decryptedGCM);
System.out.println("GCM加解密验证: " + originalText.equals(decryptedGCM));
System.out.println();
// 3. CBC模式加密解密
System.out.println("3. CBC模式加密解密测试...");
String encryptedCBC = AESCrypto.aesEncryptCBC(originalText, key);
System.out.println("CBC加密结果: " + encryptedCBC);
String decryptedCBC = AESCrypto.aesDecryptCBC(encryptedCBC, key);
System.out.println("CBC解密结果: " + decryptedCBC);
System.out.println("CBC加解密验证: " + originalText.equals(decryptedCBC));
System.out.println();
// 4. 密钥管理演示
System.out.println("4. 密钥管理功能演示...");
String testKeyId = "test_key_001";
String storedKey = AESKeyManager.generateAndStoreKey(testKeyId);
System.out.println("存储的密钥: " + storedKey);
// 5. 验证加密数据格式
System.out.println("5. 验证加密数据格式...");
System.out.println("GCM加密数据格式有效: " +
AESCrypto.isValidEncryptedData(encryptedGCM, "GCM"));
System.out.println("CBC加密数据格式有效: " +
AESCrypto.isValidEncryptedData(encryptedCBC, "CBC"));
System.out.println();
System.out.println("=== 演示完成 ===");
} catch (Exception e) {
e.printStackTrace();
}
}
}
<code_end>
<code_start project_name=java_aes_crypto filename=pom.xml title=Maven项目配置 entrypoint=false runnable=false project_final_file=true>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>aes-crypto</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- 测试框架 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.example.demo.AESDemo</mainClass>
<javaVersion>11</javaVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>
<code_end>
代码说明:
1. **完整AES加密解密实现**:提供GCM和CBC两种主流加密模式,支持AES-256加密强度
2. **密钥管理功能**:包含密钥生成、存储、导入导出等完整生命周期管理
3. **安全设计**:使用随机IV防止重复加密,GCM模式提供认证加密确保数据完整性
4. **模块化架构**:分离加密核心逻辑与密钥管理,便于维护和扩展
5. **演示程序**:包含完整的测试用例,可直接运行验证功能
主要特性包括GCM模式的认证加密、CBC模式的兼容性加密、密钥的安全存储和管理,以及完整的数据验证机制。
不推荐使用Aspect的原因:
- 执行时机较晚:AOP切面在方法调用时执行,此时可能已经执行了部分初始化逻辑
- 粒度问题:更适合业务逻辑的横切关注点,而非安全校验
- 异常处理复杂:在Aspect中抛出异常时,错误处理不如Interceptor直接
最佳实践组合:
- Interceptor:处理签名校验、权限验证等安全相关校验
- Aspect:处理日志记录、性能监控、事务管理等业务横切关注点
这样可以确保安全校验在请求处理的最早阶段完成,既保证了安全性又提升了系统性能。


被折叠的 条评论
为什么被折叠?



