李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 每日心情
  • Now
  • 系列文章
  • 论坛
  • 左邻右舍
    • 左邻右舍
    • 博友圈
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于本站
    • 网站地图
    • 网站统计
    • 另一个网站
    • 我的导航站
    • 赞助
  • 🚇开往
!Destiny
惟坚韧者始能遂其志
  1. 首页
  2. 原创
  3. 正文

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

2025年5月26日 约 1,147 字4 分钟 199 1 4
本文最后更新于 2025年5月26日,距今已 477 天,其中的信息可能已经发生变化,请注意甄别。

一、背景

在网络传输敏感数据时,接口加密是保障安全的核心手段。下面就说一说如何在 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

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
分享到

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

也可使用浏览器菜单中的「分享」功能

微信扫一扫分享

标签: AES JAVA RSA SpringBoot 加解密 安全 对称加密 非对称加密
最后更新:2025年5月26日
相关文章
  • ThreadPoolExecutor如何实现线程复用及超时销毁2021年6月18日
  • 使用AbstractRoutingDataSource动态切换数据源2025年3月15日
  • JAVA开发处理金额的数据类型你知道多少?2025年10月28日
  • 我要狠狠的反驳“公司禁止使用 Lombok ”的观点!2021年3月3日
  • Chrome 浏览器漏洞2026年8月14日

李锋镝

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

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

文章评论

  • blank
    YowaiNaegiLv 1

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

    macOSmacOSEdge 138.0.0.0Edge 138.0.0.0location日本-Hokkaido-Sapporo
    2025年7月11日
    00 回复
  • blank
    GoodBoyboyLv 2

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

    WindowsWindowsFirefox 131.0Firefox 131.0location中国-湖南-长沙
    2025年6月23日
    00 回复
  • blank
    GoodBoyboyLv 2

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

    WindowsWindowsFirefox 131.0Firefox 131.0location中国-湖南-长沙
    2025年6月23日
    00 回复
    • blank
      李锋镝博主

      @GoodBoyboy :52: :52: :52:

      WindowsWindowsChrome 137.0.0.0Chrome 137.0.0.0location中国-北京
      2025年6月23日
      00 回复
  • 惟坚韧者始能遂其志。

    听点儿音乐吧 朋友~
    文章目录
    最新 热点 随机
    最新 热点 随机
    支撑全网40%网站的WordPress正在重新拥抱PHP生态 Kratos-plus v1.1.24版本更新说明 关于主题加载速度优化的一点儿小演进 市场主流AI编程大模型横向深度分析(2026-09-11) 一款节省token的利器:RTK(Rust Token Killer) RAG太难学?LLM Wiki了解一下
    给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能WordPress缓存插件WP Fastest Cache、WP Rocket 、FlyingPress对比关于使用AI的一些思考Kratos+ v1.1.16版本更新说明AI时代,个人技术博客的出路在哪里?推荐一个SVG 矢量小图标免费下载网站
    Springboot接入DeepSeek API 详解 ZooKeeper 数据持久化 重构 Controller 终极指南:从臃肿到优雅的 7 大黄金法则 + 实战技巧 UUID太长怎么办?快来试试NanoId 写了个日期进度条的小插件 JMX监控权限认证配置
    最近评论
    blank
    lijie blog 发布于 9 小时前(09月14日) 友链申请 名称:lijie blog 链接:https://lijie.cool 描述:懒于当...
    blank
    Hary 发布于 11 小时前(09月14日) 等PHP9推倒重来,出个船新版本
    blank
    李锋镝 发布于 23 小时前(09月14日) 不愧是皮总,这措辞~ :30:
    blank
    皮皮社长 发布于 23 小时前(09月14日) :20: 我勒了个去,中国汉字波大精深。 :42:
    blank
    李锋镝 发布于 23 小时前(09月14日) 我这里面也带了一堆小表情包,看看不行也优化一波
    标签聚合
    Redis WordPress AI SpringBoot Claude 架构 JAVA 分布式 AI编程 K8s ElasticSearch Spring 多线程 日常 IDEA MySQL MQ JVM SQL 数据库
    友情链接
    • 若梦博客
    • 九仞之行
    • sssr7844的博客
    • Honesty
    • 彬红茶日记
    • 蜗牛工作室
    • 老张博客
    • 搬砖日记
    • 瓦匠个人小站
    • 志文工作室
    • 韩情脉脉
    • 知向前端
    • Serendipity
    • 临窗旋墨
    • 韩小韩博客
    • 皮皮社
    • 哥斯拉
    • Mr.Sun的博客
    • 懋和道人
    • 林羽凡

    COPYRIGHT © 2016-2026 lifengdi.com. ALL RIGHTS RESERVED.

    Domain age badge for lifengdi.com

    Theme Kratos-plus By Dylan Li

    津ICP备2024022503号-3

    京公网安备11011502039375号