Spring Boot 一个注解搞定加密 + 解密 + 签名 + 验签(一文全解)

 更新时间:2025年09月20日 09:49:35   作者:半部论语  
本文介绍了一种基于Spring Boot 3.x的接口安全解决方案,通过自定义注解@ApiSecurity结合AOP切面,实现请求解密、验签、响应加密及加签的自动化处理,_springboot api aop 报文加解密

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 {
    @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

到此这篇关于Spring Boot 一个注解搞定「加密 + 解密 + 签名 + 验签」的文章就介绍到这了,更多相关Spring Boot注解加密 解密 签名 验签内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java实现人机猜拳游戏

    Java实现人机猜拳游戏

    这篇文章主要为大家详细介绍了Java实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • logback关闭某个包的日志操作

    logback关闭某个包的日志操作

    这篇文章主要介绍了logback关闭某个包的日志操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • SpringBoot实现PDF转图片的代码示例

    SpringBoot实现PDF转图片的代码示例

    在本文中,我们使用SpringBoot演示了如何将PDF文件转换为一张或多张图片,这些示例演示了如何使用Java编程语言与其他开源技术集成,以实现各种文件格式之间的转换,感兴趣的小伙伴跟着小编一起来看看吧
    2024-08-08
  • SpringCloud turbine监控实现过程解析

    SpringCloud turbine监控实现过程解析

    这篇文章主要介绍了SpringCloud turbine监控实现过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-12-12
  • Java内存模型的深入讲解

    Java内存模型的深入讲解

    这篇文章主要给大家介绍了关于Java内存模型的相关资料,我们常说的JVM内存模式指的是JVM的内存分区,而Java内存模式是一种虚拟机规范,需要的朋友可以参考下
    2021-07-07
  • IDEA 打开java文件对应的class路径的操作步骤

    IDEA 打开java文件对应的class路径的操作步骤

    这篇文章主要介绍了IDEA 打开java文件对应的class路径的操作步骤,本文分步骤给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • Java Web会话技术Session的简单使用

    Java Web会话技术Session的简单使用

    在请求需要传递的信息比较多,使用Cookie技术就会增大请求的难度。而Session可以存储对象、数组等信息,并且Session是存储到服务器端的,在客户端请求时只需要将session id一并携带给服务器端。本文将简单的介绍如何使用Session
    2021-05-05
  • 一篇文章详解JAVA中的@Schema注解

    一篇文章详解JAVA中的@Schema注解

    @Schema注解用于描述数据模型,包括类和属性,使得描述更加的详细和清楚,通常和swagger3一起使用,这篇文章主要介绍了JAVA中@Schema注解的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • Spring Boot系列教程之日志配置

    Spring Boot系列教程之日志配置

    这篇文章主要给大家介绍了关于Spring Boot系列教程之日志配置的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-11-11
  • Java多线程基础

    Java多线程基础

    这篇文章主要介绍Java多线程基础,线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,多线程指在单个程序中可以同时运行多个不同的线程执行不同的任务,下面来学习具体的详细内容
    2021-10-10

最新评论