> 技术文档 > Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」

Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」


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 + 签名
  • 框架自动完成 解密 → 验签 → 业务处理 → 响应加密 → 加签
  • 零侵入,老接口想加安全,贴一个注解即可。

二、传输对象

@Datapublic 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 { @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return methodParameter.hasMethodAnnotation(ApiSecurity.class) && methodParameter.getMethodAnnotation(ApiSecurity.class).decryptRequest(); } @Override public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } @SneakyThrows @Override public 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 解密业务 JSON String 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> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { ApiSecurity anno = returnType.getMethodAnnotation(ApiSecurity.class); return anno != null && anno.encryptResponse(); } @SneakyThrows @Override public 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; }}

五、工具类速览

  • RSAUtilencrypt/decrypt + sign/verify
  • AESUtilencrypt/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