李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 左邻右舍
  • 博友圈
  • 关于我
    • 关于我
    • 另一个网站
    • 我的导航站
    • 网站地图
    • 赞助
  • 留言
  • 走心评论
  • 系列文章
  • Now
  • 每日心情
  • 🚇开往
Destiny
自是人生长恨水长东
  1. 首页
  2. 原创
  3. 正文

SpringBoot 实现 RSA+AES 自动接口解密

2025年5月26日 约 1,147 字4 分钟 63点热度 1人点赞 4条评论
本文最后更新于 2025年5月26日,距今已 440 天,其中的信息可能已经发生变化,请注意甄别。

一、背景

在网络传输敏感数据时,接口加密是保障安全的核心手段。下面就说一说如何在 SpringBoot 中实现 RSA+AES 混合加密方案,同时结合两种算法的优势,确保数据传输的安全性与高效性。

二、加密方案优势

算法 特点 适用场景
RSA 非对称加密,安全性高,但加密速度慢,适合加密少量数据(如密钥)。 加密 AES 密钥
AES 对称加密,速度快,适合大量数据加密,但密钥分发困难。 加密实际传输数据
混合方案 RSA 加密 AES 密钥 + AES 加密业务数据,兼顾安全与性能。 敏感数据接口通信

三、实现原理

  1. 客户端流程:

    • 生成随机 AES 密钥。
    • 用 RSA 公钥 加密 AES 密钥。
    • 用 AES 密钥加密业务数据,附带初始化向量(IV)和时间戳。
    • 发送加密后的 AES 密钥、加密数据、IV、时间戳至服务端。
  2. 服务端流程:

    • 用 RSA 私钥 解密获取 AES 密钥。
    • 用 AES 密钥解密业务数据,验证时间戳防重放攻击。

四、项目依赖(pom.xml)

<dependencies>  
    <dependency>  
        <groupId>org.springframework.boot</groupId>  
        <artifactId>spring-boot-starter-web</artifactId>  
    </dependency>  
    <dependency>  
        <groupId>org.projectlombok</groupId>  
        <artifactId>lombok</artifactId>  
    </dependency>  
    <dependency>  
        <groupId>com.alibaba</groupId>  
        <artifactId>fastjson</artifactId>  
        <version>1.2.78</version>  
    </dependency>  
    <dependency>  
        <groupId>org.bouncycastle</groupId>  
        <artifactId>bcprov-jdk15on</artifactId>  
        <version>1.68</version>  
    </dependency>  
</dependencies>  

五、核心代码实现

1. 加密工具类(EncryptionUtils)

package com.example.secureapi.utils;  
// 省略导入包  

public class EncryptionUtils {  
    static { Security.addProvider(new BouncyCastleProvider()); }  
    private static final String AES_ALGORITHM = "AES/CBC/PKCS7Padding";  
    private static final String RSA_ALGORITHM = "RSA/ECB/PKCS1Padding";  

    // 生成 RSA 密钥对  
    public static KeyPair generateRSAKeyPair() throws Exception {  
        KeyPairGenerator.getInstance("RSA").initialize(2048);  
        return keyPairGenerator.generateKeyPair();  
    }  

    // RSA 加密/解密  
    public static String encryptWithRSA(String data, PublicKey publicKey) throws Exception {  
        Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));  
    }  
    public static String decryptWithRSA(String encryptedData, PrivateKey privateKey) throws Exception {  
        // 解密逻辑类似加密,使用私钥初始化Cipher  
    }  

    // AES 加密/解密(需初始化向量 IV)  
    public static String encryptWithAES(String data, SecretKey secretKey, byte[] iv) throws Exception {  
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM, "BC");  
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));  
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes()));  
    }  
    public static String decryptWithAES(String encryptedData, SecretKey secretKey, byte[] iv) throws Exception {  
        // 解密逻辑类似加密,使用 DECRYPT_MODE  
    }  

    // 生成随机 AES 密钥和 IV  
    public static SecretKey generateAESKey() throws Exception {  
        KeyGenerator.getInstance("AES").init(256);  
        return keyGen.generateKey();  
    }  
    public static byte[] generateIV() {  
        byte[] iv = new byte[16]; new SecureRandom().nextBytes(iv); return iv;  
    }  
}  

2. 请求包装类(EncryptedRequest)

package com.example.secureapi.model;  
import lombok.Data;  

@Data  
public class EncryptedRequest {  
    private String encryptedKey;   // RSA 加密后的 AES 密钥  
    private String iv;             // Base64 编码的 AES 初始化向量  
    private String encryptedData;  // AES 加密后的业务数据  
    private Long timestamp;        // 时间戳(防重放攻击)  
    private String signature;      // 签名(防篡改)  
}  

3. 解密拦截器(DecryptInterceptor)

package com.example.secureapi.interceptor;  
// 省略导入包  

@Slf4j  
@Component  
public class DecryptInterceptor implements HandlerInterceptor {  
    @Value("${security.rsa.private-key}") private String rsaPrivateKeyStr;  

    @Override  
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
        if (!(handler instanceof HandlerMethod)) return true;  
        HandlerMethod handlerMethod = (HandlerMethod) handler;  
        Decrypt decryptAnnotation = handlerMethod.getMethodAnnotation(Decrypt.class);  
        if (decryptAnnotation == null) {  
            decryptAnnotation = handlerMethod.getBeanType().getAnnotation(Decrypt.class);  
        }  
        if (decryptAnnotation != null) {  
            // 解析请求体  
            String requestBody = request.getReader().lines().collect(Collectors.joining());  
            EncryptedRequest encryptedReq = JSON.parseObject(requestBody, EncryptedRequest.class);  

            // 解密 AES 密钥  
            PrivateKey rsaPrivateKey = EncryptionUtils.stringToRSAPrivateKey(rsaPrivateKeyStr);  
            String aesKeyStr = EncryptionUtils.decryptWithRSA(encryptedReq.getEncryptedKey(), rsaPrivateKey);  
            SecretKey aesKey = EncryptionUtils.stringToAESKey(aesKeyStr);  

            // 解密业务数据  
            byte[] iv = Base64.getDecoder().decode(encryptedReq.getIv());  
            String decryptedData = EncryptionUtils.decryptWithAES(encryptedReq.getEncryptedData(), aesKey, iv);  

            // 防重放攻击检查  
            if (encryptedReq.getTimestamp() != null &&  
                Math.abs(System.currentTimeMillis() - encryptedReq.getTimestamp()) > 300000) {  
                response.setStatus(403);  
                response.getWriter().write("{code:403,message:请求已过期}");  
                return false;  
            }  

            // 包装请求体为解密后的数据  
            request.setAttribute(DECRYPTED_DATA, decryptedData);  
            return wrapRequest(request, decryptedData);  
        }  
        return true;  
    }  

    // 自定义请求包装类,替换原始请求体  
    private static class DecryptedRequestWrapper extends HttpServletRequestWrapper {  
        private final String decryptedData;  
        public DecryptedRequestWrapper(HttpServletRequest request, String decryptedData) {  
            super(request);  
            this.decryptedData = decryptedData;  
        }  
        // 重写 getReader() 和 getInputStream() 方法,返回解密后的数据  
    }  
}  

4. 解密注解(@Decrypt)

package com.example.secureapi.annotation;  
import java.lang.annotation.*;  

@Target({ElementType.METHOD, ElementType.TYPE})  
@Retention(RetentionPolicy.RUNTIME)  
public @interface Decrypt {  
    boolean checkTimestamp() default true;  // 是否检查时间戳  
    boolean verifySignature() default false; // 是否验证签名  
}  

5. 拦截器配置(WebMvcConfig)

package com.example.secureapi.config;  
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  

@Configuration  
public class WebMvcConfig implements WebMvcConfigurer {  
    @Autowired private DecryptInterceptor decryptInterceptor;  

    @Override  
    public void addInterceptors(InterceptorRegistry registry) {  
        registry.addInterceptor(decryptInterceptor).addPathPatterns(/api/**);  
    }  
}  

六、使用方式

在需要解密的控制器或方法上添加 @Decrypt 注解:

@RestController  
@RequestMapping(/api)  
@Decrypt  // 类级别注解,作用于所有方法  
public class SecureController {  
    @PostMapping(/data)  
    public String processData(@RequestBody String decryptedData) {  
        // 直接处理解密后的明文数据  
        return 处理结果: + decryptedData;  
    }  
}  

七、总结

  • 优势:RSA+AES 混合加密结合了非对称加密的安全性与对称加密的高效性,适用于敏感数据传输场景。
  • 关键点:通过拦截器自动解密请求体,利用注解灵活控制需要加密的接口,同时支持时间戳防重放和签名防篡改。
  • 扩展:可进一步添加签名验证(如 HMAC-SHA256),确保数据完整性。
除非注明,否则均为李锋镝的博客原创文章,转载必须以链接形式标明本文链接

本文链接:https://www.lifengdi.com/article/4428

推荐阅读

  • @Valid 和 @Validated 的区别
  • SpringBoot常用注解
  • CompletableFuture使用详解
  • SpringBoot 中内置的 49 个常用工具类
  • SpringBoot 实现接口防刷的 5 种实现方案
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
标签: AES JAVA RSA SpringBoot 加解密 安全 对称加密 非对称加密
最后更新:2025年5月26日

岁月同一天 8 月 9 日

回望过去的今天,你在写什么

  • 4 年前 2022年8月9日
    RocketMQ的push消费方式实现详解

    MQ消费方式 消费方式就是指消费者如何从MQ中获取到消息,分为两种方式,push(推方式)和pull(拉方式)。 1、p…

相关文章
  • 从零开始入门 K8s | Kubernetes 网络概念及策略控制2019年10月17日
  • 分布式锁-Java常用技术方案2019年11月11日
  • 8条经验轻松上手IDEA插件开发2020年11月6日
  • Java设计模式:状态模式2025年5月22日
  • 记一次Apollo配置中心+Spring配置自动刷新导致的内存泄露问题2026年7月31日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 上一篇
下一篇 >
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复
…

文章评论

  • YowaiNaegiLv 1

    您好,,关于DecryptInterceptor中使用了EncryptionUtils.stringToRSAPrivateKey()以及EncryptionUtils.stringToAESKey(),但是在EncryptionUtils中并没有这两个方法,还请麻烦大佬能补充一下,谢谢!

    macOSEdge 138.0.0.0 日本-Sapporo
    2025年7月11日
    00 回复
  • GoodBoyboyLv 2

    像用类似ECDHE来生成共享密钥,再派生出aes所需要的密钥和向量

    WindowsFirefox 131.0 中国-长沙市
    2025年6月23日
    00 回复
  • GoodBoyboyLv 2

    可以试试密钥派生的方法,避免密钥在网络上传输

    WindowsFirefox 131.0 中国-长沙市
    2025年6月23日
    00 回复
    • 李锋镝管理

      @GoodBoyboy :52: :52: :52:

      WindowsChrome 137.0.0.0 中国-北京市
      2025年6月23日
      00 回复
  • 文章合为时而著,歌诗合为事而作。

    听点儿音乐吧 朋友~
    文章目录
    最新 热点 随机
    最新 热点 随机
    Kratos+ v1.1.14版本更新说明 Spring Boot 指定外部配置文件的方式 Spring Boot 配置加载优先级总结 Claude Fable 5(claude-fable-5)深度详解 如何通过命令查看Java应用内存中对象数量 关于服务的探活端口和业务端口不一致有什么问题
    给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能AI时代,个人技术博客的出路在哪里?增加了两套复古皮肤-牛皮纸、千禧网页这个域名注册整整十年了,十年时间,真快啊WordPress实现用户评论等级排行榜插件WordPress网站换了个字体,差点儿把样式换崩了
    使用RocketMQ时,服务启动过程中,Consumer在服务未启动时消费消息问题处理 JVM详细参数说明 Kratos+ —— Kratos 主题二次开发记录 MySQL语句执行顺序 一款开源的社交分享插件——share.js 笑死、腹肌……根本不可能有腹肌的~~
    最近评论
    老张博客 发布于 16 小时前(08月08日) 做的越来越精美了,好看。
    Huo 发布于 1 天前(08月07日) 挺有特色的主题,还是感觉 WP 的确是强大
    李锋镝 发布于 2 天前(08月07日) 没理解你想说啥
    aboss 发布于 2 天前(08月07日) 你的后台web-login?
    李锋镝 发布于 2 天前(08月07日) 这个专门的插件实现的功能更好更全,还能对接支付之类的
    标签聚合
    数据库 WordPress docker Claude JVM 分布式 日常 ElasticSearch K8s AI SpringBoot 架构 AI编程 Spring MySQL SQL JAVA 多线程 IDEA Redis
    友情链接
    • 志文工作室
    • 皮皮社
    • Mr.Sun的博客
    • 风渡言
    • 韩小韩博客
    • 搬砖日记
    • 临窗旋墨
    • 知向前端
    • 哥斯拉
    • Blogs·CN
    • 瓦匠个人小站
    • 旧时繁华
    • 彬红茶日记
    • Honesty
    • 韩情脉脉
    • 林羽凡
    • 老张博客
    • 拾趣博客导航
    • 懋和道人

    COPYRIGHT © 2026 lifengdi.com. ALL RIGHTS RESERVED.

    正在博友圈履约中

    域名年龄

    Theme Kratos+ By Dylan Li

    津ICP备2024022503号-3

    京公网安备11011502039375号