2026年01月19日/ 浏览 11
要在 Spring Boot 中通过一个注解实现加解密与签名功能,我们可以利用AOP(面向切面编程) 来拦截注解标注的方法,在方法执行前后处理加解密和签名逻辑。

在pom.xml中引入 Spring AOP、加密相关依赖:
<dependencies> <!-- Spring Boot Starter AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- 加密相关(如BouncyCastle,可选) --> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk15on</artifactId> <version>1.70</version> </dependency> <!-- JSON处理(如FastJSON) --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>2.0.32</version> </dependency> </dependencies>定义注解,用于标记需要处理加解密和签名的方法:
import java.lang.annotation.*; /** * 加解密与签名注解 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface CryptoSign { /** * 加密算法类型(如AES、RSA) */ String encryptAlgorithm() default "AES"; /** * 签名算法类型(如HmacSHA256、RSA) */ String signAlgorithm() default "HmacSHA256"; /** * 是否验证请求签名 */ boolean verifySign() default true; }封装 AES 加密、HmacSHA256 签名等工具方法(可根据需求扩展 RSA 等算法):
import javax.crypto.Cipher; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.Key; import java.security.MessageDigest; import java.security.Security; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * 加解密与签名工具类 */ public class CryptoUtils { // 密钥(实际项目中建议从配置文件读取,且定期轮换) private static final String AES_KEY = "1234567890123456"; // AES-128需16位密钥 private static final String HMAC_KEY = "hmacKey1234567890"; // Hmac密钥 static { // 注册BouncyCastleProvider(可选,用于扩展算法) Security.addProvider(new BouncyCastleProvider()); } /** * AES加密 */ public static String aesEncrypt(String content) throws Exception { Key key = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); return bytesToHex(encryptBytes); } /** * AES解密 */ public static String aesDecrypt(String encryptContent) throws Exception { Key key = new SecretKeySpec(AES_KEY.getBytes(StandardCharsets.UTF_8), "AES"); Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptBytes = cipher.doFinal(hexToBytes(encryptContent)); return new String(decryptBytes, StandardCharsets.UTF_8); } /** * HmacSHA256签名 */ public static String hmacSHA256Sign(String content) throws Exception { SecretKeySpec keySpec = new SecretKeySpec(HMAC_KEY.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(keySpec); byte[] signBytes = mac.doFinal(content.getBytes(StandardCharsets.UTF_8)); return bytesToHex(signBytes); } /** * 验证HmacSHA256签名 */ public static boolean verifyHmacSHA256Sign(String content, String sign) throws Exception { String calculateSign = hmacSHA256Sign(content); return calculateSign.equalsIgnoreCase(sign); } /** * 字节数组转16进制字符串 */ public static String bytesToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (byte b : bytes) { sb.append(String.format("%02x", b)); } return sb.toString(); } /** * 16进制字符串转字节数组 */ public static byte[] hexToBytes(String hex) { int len = hex.length(); byte[] bytes = new byte[len / 2]; for (int i = 0; i < len; i += 2) { bytes[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); } return bytes; } /** * MD5摘要(可选,用于辅助签名) */ public static String md5(String content) throws Exception { MessageDigest md = MessageDigest.getInstance("MD5"); byte[] md5Bytes = md.digest(content.getBytes(StandardCharsets.UTF_8)); return bytesToHex(md5Bytes); } }编写切面类,拦截@CryptoSign注解的方法,处理入参解密、出参加密和签名验证:
import com.alibaba.fastjson.JSON; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import java.lang.reflect.Method; import java.util.Map; /** * 加解密与签名切面 */ @Aspect @Component public class CryptoSignAspect { /** * 定义切入点:拦截标注了@CryptoSign的方法 */ @Pointcut("@annotation(com.example.demo.annotation.CryptoSign)") public void cryptoSignPointcut() {} /** * 环绕通知:处理加解密与签名逻辑 */ @Around("cryptoSignPointcut()") public Object around(ProceedingJoinPoint joinPoint) throws Throwable { // 1. 获取注解信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); CryptoSign cryptoSign = method.getAnnotation(CryptoSign.class); String encryptAlgorithm = cryptoSign.encryptAlgorithm(); String signAlgorithm = cryptoSign.signAlgorithm(); boolean verifySign = cryptoSign.verifySign(); // 2. 处理入参解密与签名验证 Object[] args = joinPoint.getArgs(); if (args != null && args.length > 0) { for (int i = 0; i < args.length; i++) { Object arg = args[i]; if (arg instanceof Map) { // 假设入参为Map,包含加密内容和签名(实际可根据业务调整) Map<String, String> paramMap = (Map<String, String>) arg; String encryptContent = paramMap.get("content"); String sign = paramMap.get("sign"); // 解密内容 String decryptContent; if ("AES".equals(encryptAlgorithm)) { decryptContent = CryptoUtils.aesDecrypt(encryptContent); } else { throw new UnsupportedOperationException("不支持的加密算法:" + encryptAlgorithm); } // 验证签名 if (verifySign) { boolean signValid; if ("HmacSHA256".equals(signAlgorithm)) { signValid = CryptoUtils.verifyHmacSHA256Sign(decryptContent, sign); } else { throw new UnsupportedOperationException("不支持的签名算法:" + signAlgorithm); } if (!signValid) { throw new SecurityException("签名验证失败"); } } // 替换入参为解密后的内容(实际可根据业务调整,如转为实体类) args[i] = decryptContent; } } } // 3. 执行目标方法 Object result = joinPoint.proceed(args); // 4. 处理出参加密与签名 if (result != null) { // 将结果转为JSON字符串(实际可根据业务调整) String resultStr = JSON.toJSONString(result); // 加密内容 String encryptResult; if ("AES".equals(encryptAlgorithm)) { encryptResult = CryptoUtils.aesEncrypt(resultStr); } else { throw new UnsupportedOperationException("不支持的加密算法:" + encryptAlgorithm); } // 生成签名 String sign; if ("HmacSHA256".equals(signAlgorithm)) { sign = CryptoUtils.hmacSHA256Sign(resultStr); } else { throw new UnsupportedOperationException("不支持的签名算法:" + signAlgorithm); } // 构造响应结果(包含加密内容和签名) return Map.of("encryptContent", encryptResult, "sign", sign); } return result; } }编写 Controller 或 Service 方法,使用@CryptoSign注解标记:
import com.example.demo.annotation.CryptoSign; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; import java.util.Map; @RestController public class CryptoController { /** * 测试加解密与签名接口 */ @PostMapping("/testCrypto") @CryptoSign(encryptAlgorithm = "AES", signAlgorithm = "HmacSHA256", verifySign = true) public String testCrypto(@RequestBody Map<String, String> param) { // 入参已被切面解密,此处直接处理业务逻辑 String content = (String) param; // 实际切面中已替换入参为解密后的字符串 return "处理结果:" + content; } }通过以上实现,即可在 Spring Boot 中通过一个注解快速实现方法级别的加解密与签名功能,且具备良好的扩展性和可维护性。