Spring Boot 一個注解搞定「加密 + 解密 + 簽名 + 驗簽」
本文基于 Spring Boot 3.x,通過一個自定義注解 + AOP,一行注解即可給任何 Controller 方法加上
請求解密 → 驗簽 → 響應加密 → 加簽 的完整鏈路,并可直接拷貝到生產環境使用。
一、最終效果
@PostMapping("/order")
@ApiSecurity(decryptRequest = true, encryptResponse = true) // ← 就這么一行
public OrderResp createOrder(@RequestBody OrderReq req) {return service.create(req);
}
- 請求體:RSA 加密后的 AES 密鑰 + AES 加密后的業務 JSON + 簽名
- 框架自動完成 解密 → 驗簽 → 業務處理 → 響應加密 → 加簽
- 零侵入,老接口想加安全,貼一個注解即可。
二、傳輸對象
@Data
public class ApiSecurityParam {private String appId; // 應用標識private String key; // RSA 加密后的 AES 密鑰(Base64)private String data; // AES 加密的業務 JSON(Base64)private String sign; // 簽名private String timestamp; // 防重放private String nonce; // 防重放
}
三、核心注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSecurity {boolean decryptRequest() default false; // 請求體是否解密boolean encryptResponse() default false; // 響應體是否加密boolean sign() default true; // 是否驗簽/加簽
}
四、AOP 切面(RequestBodyAdvice + ResponseBodyAdvice)
同時解決 InputStream 只能讀一次 的問題。
4.1 解密 & 驗簽 RequestBodyAdvice
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DecryptRequestAdvice implements RequestBodyAdvice {@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(ApiSecurity.class)&& methodParameter.getMethodAnnotation(ApiSecurity.class).decryptRequest();}@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage,MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {return body;}@SneakyThrows@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage,MethodParameter parameter, Type targetType,Class<? extends HttpMessageConverter<?>> converterType) {String body = StreamUtils.copyToString(inputMessage.getBody(), StandardCharsets.UTF_8);ApiSecurityParam param = JSON.parseObject(body, ApiSecurityParam.class);// 1. 防重放校驗(timestamp、nonce)checkReplay(param);// 2. RSA 私鑰解密 AES 密鑰String aesKey = RSAUtil.decryptByPrivateKey(param.getKey(), RsaKeyHolder.PRIVATE_KEY);// 3. AES 解密業務 JSONString json = AESUtil.decrypt(param.getData(), aesKey);// 4. 驗簽boolean ok = RSAUtil.verify(json + param.getTimestamp() + param.getNonce(),RsaKeyHolder.PUBLIC_KEY, param.getSign());if (!ok) throw new BizException("驗簽失敗");return new MappingJacksonInputMessage(new ByteArrayInputStream(json.getBytes()),inputMessage.getHeaders());}
}
4.2 加密 & 加簽 ResponseBodyAdvice
@RestControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class EncryptResponseAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter returnType,Class<? extends HttpMessageConverter<?>> converterType) {ApiSecurity anno = returnType.getMethodAnnotation(ApiSecurity.class);return anno != null && anno.encryptResponse();}@SneakyThrows@Overridepublic Object beforeBodyWrite(Object body, MethodParameter returnType,MediaType selectedContentType,Class<? extends HttpMessageConverter<?>> selectedConverterType,ServerHttpRequest request, ServerHttpResponse response) {String json = JSON.toJSONString(body);// 1. 隨機 AES 密鑰String aesKey = AESUtil.randomKey(128);// 2. AES 加密響應String data = AESUtil.encrypt(json, aesKey);// 3. RSA 公鑰加密 AES 密鑰String encKey = RSAUtil.encryptByPublicKey(aesKey, RsaKeyHolder.PUBLIC_KEY);// 4. 生成簽名String sign = RSAUtil.sign(json, RsaKeyHolder.PRIVATE_KEY);ApiSecurityParam resp = new ApiSecurityParam();resp.setKey(encKey);resp.setData(data);resp.setSign(sign);resp.setTimestamp(String.valueOf(System.currentTimeMillis()));return resp;}
}
五、工具類速覽
- RSAUtil:
encrypt/decrypt
+sign/verify
- AESUtil:
encrypt/decrypt
支持 PKCS5Padding - RsaKeyHolder:從
application.yml
或 KMS 讀取公私鑰
六、性能 & 安全小貼士
點 | 建議 |
---|---|
對稱加密 | AES-128-CBC/PKCS5Padding |
非對稱 | RSA-2048 |
防重放 | timestamp ±5 min + nonce 一次性 |
密鑰輪換 | 每日定時任務刷新 RSA 密鑰對 |
性能 | AES 每次隨機 IV,RSA 只加密 128bit 密鑰,無壓力 |
七、小結
通過以上 一個注解 + 兩個 Advice,在 Spring Boot 中實現 企業級安全傳輸:
- 0 侵入:老接口貼注解即可
- 高可擴展:支持 GET/POST、Header 傳參、自定義算法
- 已落地:可直接封裝為
spring-boot-starter-security-api
,全公司復用。
源碼示例已上傳 GitHub:
https://github.com/your-org/spring-boot-api-security-starter