Spring Boot 应用中实现配置文件敏感信息加密解密方案

article/2025/8/7 11:28:24

Spring Boot 应用中实现配置文件敏感信息加密解密方案

  • 背景与挑战 🚩
  • 一、设计目标 🎯
  • 二、整体启动流程 🔄
  • 三、方案实现详解 ⚙️
    • 3.1 配置解密入口:`EnvironmentPostProcessor`
    • 3.2 通用解密工具类:`EncryptionTool`
  • 四、快速上手指南 ⚡
    • 4.1 依赖引入
    • 4.2 注册 `EnvironmentPostProcessor`
    • 4.3 生成密钥
    • 4.4 配置示例
    • 4.5 启动注入密钥
  • 五、安全最佳实践 🔐
  • 六、总结 🎉


背景与挑战 🚩

在现代企业级应用中,application.ymlapplication.properties 常用于配置数据库(DataSource)、Redis、RabbitMQ 等中间件的连接信息。

spring:datasource:username: myuserpassword: my-secret-password

但问题来了:

将明文密码直接写入配置文件中存在诸多风险,主要包括:

❌ 风险类型详细描述
代码仓库泄露风险配置文件可能被误提交到 Git 等版本管理系统,导致敏感信息外泄。
构建与发布风险打包过程或日志文件可能暴露敏感数据,带来安全隐患。
调试与共享风险第三方人员或调试时可能接触到明文,增加信息暴露概率。

因此,敏感信息必须避免以明文形式存储。

一、设计目标 🎯

目标说明
🔒 零明文配置配置文件中敏感字段均以 ENC(...) 形式存储,无明文密码。
⚙️ 自动解密应用启动时自动解密,业务代码无感知,无需改动。
🔄 多算法支持兼容 RSA、AES 等主流加密算法,满足不同安全需求。
🎛️ 开关灵活支持配置及环境变量动态启停解密功能,满足多环境多场景。
🤝 无侵入业务代码保持 Spring Boot 原生配置机制,业务层透明使用解密后的配置。

二、整体启动流程 🔄

  1. 密钥注入

    通过环境变量(如 DB_SECRET_KEY)注入 RSA 私钥或对称密钥。

  2. EnvironmentPostProcessor 扫描

    Spring Boot 启动时自动加载实现了 EnvironmentPostProcessor 的解密组件。

  3. 配置源扫描

    遍历所有 PropertySource,查找形如 ENC(...) 的密文字段。

  4. 调用解密工具

    根据配置的算法(RSA/AES)还原明文。

  5. 注入环境变量

    将解密后的结果以 MapPropertySource 形式优先加载,覆盖原加密值。

  6. 后续加载

    DataSource、Redis、RabbitMQ 等配置自动获得解密后的明文。

在这里插入图片描述

三、方案实现详解 ⚙️

3.1 配置解密入口:EnvironmentPostProcessor

利用 Spring Boot 启动机制的 EnvironmentPostProcessor,启动早期扫描并解密所有配置文件中的敏感字段。

@Slf4j
public class DecryptEnvPostProcessor implements EnvironmentPostProcessor {// 预定义需要解密的配置项 key,只对这些 key 进行解密处理private static final Set<String> ENCRYPTED_KEYS = Set.of("spring.datasource.password","custom.service.password");@Overridepublic void postProcessEnvironment(ConfigurableEnvironment env, SpringApplication app) {boolean enabled = Boolean.parseBoolean(env.getProperty("config.decrypt.enabled", "true"));if (!enabled) {log.info("配置解密功能已关闭,跳过解密流程");return;}String key = System.getenv("DB_SECRET_KEY");if (StringUtils.isBlank(key)) {throw new IllegalStateException("缺少解密密钥(DB_SECRET_KEY),无法完成解密");}Map<String, Object> decryptedValues = new HashMap<>();for (PropertySource<?> source : env.getPropertySources()) {if (source instanceof EnumerablePropertySource<?> eps) {for (String name : eps.getPropertyNames()) {if (ENCRYPTED_KEYS.contains(name)) {Object val = eps.getProperty(name);if (val instanceof String s && s.startsWith("ENC(") && s.endsWith(")")) {String cipherText = s.substring(4, s.length() - 1);try {String plainText = EncryptionTool.decrypt(key, cipherText, "RSA");decryptedValues.put(name, plainText);} catch (Exception e) {log.warn("解密配置项 [{}] 失败,保持原密文", name, e);}}}}}}if (!decryptedValues.isEmpty()) {env.getPropertySources().addFirst(new MapPropertySource("decryptedProperties", decryptedValues));}log.info("配置文件敏感信息解密完成");}
}

关键点说明:

  • 配置扫描与解密:支持 YAML、properties、环境变量等多种配置源。

  • 解密开关灵活控制:通过 config.decrypt.enabled 配置项动态启用或禁用解密逻辑。

  • 优先级注入:通过 addFirst 优先注入解密后的配置,确保后续 Bean 读取时获得明文。

  • 异常安全:解密异常仅警告,保证启动流程不受阻断。

拓展建议

  • 建议将 ENCRYPTED_KEYS 设计为项目可配置项,甚至支持通配符或注解形式,提高灵活性。

  • addFirst 保证解密后的配置覆盖原加密内容,确保业务读取到明文。

3.2 通用解密工具类:EncryptionTool

支持多种主流加密算法,默认实现 RSA 和 AES,使用 Base64 作为密钥和密文的编码方式。

public class EncryptionTool {private static final String RSA = "RSA";public static String decrypt(String key, String cipherText, String algorithm) throws Exception {if (RSA.equalsIgnoreCase(algorithm)) {return decryptByPrivateKey(cipherText, key);} else {SecretKey secretKey = decodeKey(key, algorithm);Cipher cipher = Cipher.getInstance(algorithm);cipher.init(Cipher.DECRYPT_MODE, secretKey);byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(cipherText));return new String(decrypted, StandardCharsets.UTF_8);}}private static String decryptByPrivateKey(String cipherText, String base64PrivateKey) throws Exception {byte[] keyBytes = Base64.getDecoder().decode(base64PrivateKey);PrivateKey privateKey = KeyFactory.getInstance(RSA).generatePrivate(new PKCS8EncodedKeySpec(keyBytes));Cipher cipher = Cipher.getInstance(RSA);cipher.init(Cipher.DECRYPT_MODE, privateKey);byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(cipherText));return new String(decryptedBytes, StandardCharsets.UTF_8);}private static SecretKey decodeKey(String encodedKey, String algorithm) {byte[] decodedKey = Base64.getDecoder().decode(encodedKey);return new SecretKeySpec(decodedKey, algorithm);}
}

拓展建议

  • 可实现更多算法,如 DESede(3DES)、ChaCha20,满足不同安全合规需求。

  • 对于性能敏感场景,可考虑解密缓存策略。

四、快速上手指南 ⚡

4.1 依赖引入

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId>
</dependency>

4.2 注册 EnvironmentPostProcessor

  1. 在Spring Boot 项目的 resources 目录下添加一个文件

    src/main/resources/META-INF/spring.factories
    

    ⚠️ 注意:路径和文件名都必须完全正确!

  2. 文件内容示例

    org.springframework.boot.env.EnvironmentPostProcessor=\
    com.example.config.DecryptionEnvironmentPostProcessor
    
    • com.example.config.DecryptionEnvironmentPostProcessor 替换成你自己的类的完整包名。

    • 逗号分隔可以注册多个 EnvironmentPostProcessor

    • 必须没有拼写错误,且类必须能被 Spring Boot classpath 加载。

  3. 示例项目结构(最小可运行)

    your-project/
    ├── src/
    │   └── main/
    │       ├── java/
    │       │   └── com/example/config/
    │       │       └── DecryptionEnvironmentPostProcessor.java
    │       └── resources/
    │           └── META-INF/
    │               └── spring.factories
    ├── pom.xml
    

4.3 生成密钥

算法说明工具示例
RSA生成一对公私钥,私钥需 PKCS#8 格式 Base64 编码OpenSSL, Keytool
AES生成 128/256 位随机密钥,Base64 编码OpenSSL, Java KeyGenerator

4.4 配置示例

spring:datasource:username: db_userpassword: ENC(rGA1bK3t...EncryptedText...)config:decrypt:enabled: true

4.5 启动注入密钥

export DB_SECRET_KEY=$(cat /etc/secure/rsa_private_key.pem)
java -jar app.jar --spring.profiles.active=prod

五、安全最佳实践 🔐

建议说明
🔑 专业密钥管理使用 Vault、AWS KMS、Azure Key Vault 等专业平台管理密钥,杜绝硬编码及磁盘持久化。
🛡️ 最小权限原则严格限制密钥环境变量或文件权限,避免非授权访问。
📜 日志审计控制绝不在日志中输出明文或解密结果,防止敏感信息泄露。
🔄 定期密钥轮换定期更新密钥,缩短密钥生命周期,降低风险。
🔍 分级加密策略针对不同环境/服务使用独立密钥,降低横向攻击风险。

六、总结 🎉

借助本方案,可以实现:

  • 🕵️‍♂️ 配置文件零明文:彻底消除明文密码泄露风险

  • 🚀 启动自动解密:业务代码无侵入,透明使用明文配置

  • 🔄 多算法灵活支持:满足多场景安全合规需求

  • 🎛️ 开关灵活控制:方便多环境适配,快速切换

  • 🛡️ 安全规范完善:符合企业级安全管理最佳实践

本方案不仅满足高安全标准,还保持了 Spring Boot 配置体系的自然兼容与开发便利性。建议结合项目实际,进一步扩展支持密钥动态更新、配置加密校验等高级特性。


http://www.hkcw.cn/article/hTBmjlEVYu.shtml

相关文章

前端实现图片压缩:基于 HTML5 File API 与 Canvas 的完整方案

在 Web 开发中,处理用户上传的图片时,前端压缩可以有效减少服务器压力并提升上传效率。本文将详细讲解如何通过<input type="file">实现图片上传,结合 Canvas 实现图片压缩,并实时展示压缩前后的图片预览和文件大小对比。 一、核心功能架构 我们将实现以…

用wireshark抓了个TCP通讯的包

昨儿个整理了下怎么用wireshark抓包&#xff0c;链接在这里&#xff1a;捋捋wireshark 今天打算抓个TCP通讯的包试试&#xff0c;整体来说比较有收获&#xff0c;给大家汇报一下。 首先就是如何搞到可以用来演示TCP通讯的客户端、服务端&#xff0c;问了下deepseek&#xff0c;…

AWS WAF设置IP白名单

目标 设置一个组白名单IP地址&#xff0c;当发现是这些IP地址发过来的请求后&#xff0c;WAF自动放行。 创建IP集 打开WAF页面&#xff0c;开始IP集创建如下图&#xff1a; 设置ip集&#xff0c;如下图&#xff1a; aws waf acl配置白名单 找到Web ACL&#xff0c;开始在…

随笔20250530 C# 整合 IC卡读写技术解析与实现

以下是一个完整、最简化的 FeliCa 读取整合示例&#xff08;无需 SDK&#xff0c;基于 PCSC NuGet 包&#xff09;&#xff0c;你可以直接运行这个控制台程序&#xff0c;验证能否识别 RC-S300 并读取卡片 UID&#xff1a; &#x1f9ea; 示例说明 &#x1f4e6; 使用 NuGet 包…

day024-网络基础-TCP与UDP、DNS

文章目录 1. 李导推荐书籍2. OSI七层模型2.1 传输层2.2 网络层2.2.1 问&#xff1a;两端处于不同局域网的设备怎么网络通信&#xff1f; 2.3 数据链路层2.4 物理层2.5 图解OSI七层模型 3. 数据传输模式3.1 全双工3.2 半双工3.3 单工 4. TCP 3次握手4.1 抓包 5. TCP 4次挥手5.1 …

AI赋能开源:如何借助MCP快速解锁开源项目并提交你的首个PR

引子 很多同学都梦想为开源项目贡献力量&#xff0c;然而现实往往是——面对庞大复杂的项目&#xff0c;从入门到提交第一个有实质性代码的PR&#xff0c;时间跨度可能长达数年。传统路径通常是先从文档贡献开始&#xff0c;逐步深入理解项目架构&#xff0c;最终才能进行代码…

智能问数技术路径对比:NL2SQL vs NL2Semantic2SQL

在人工智能浪潮席卷数据分析领域的当下&#xff0c;“智能问数”凭借其自然语言交互的便捷性&#xff0c;迅速成为企业提升数据民主化与决策效率的焦点。大语言模型&#xff08;LLM&#xff09;展现出的强大语言理解和生成能力&#xff0c;无疑为这一愿景启动了引擎。 然而&am…

QT-Creator安装教程(windows)

目录 1&#xff0c;下载 1.1 镜像源下载 1.2 运行下载的exe文件 1.2.1 QT5 版本安装 1.2.2 QT6 版本安装 1.2.3 如何在安装完成之后&#xff0c;继续添加扩展包 1&#xff0c;下载 1.1 镜像源下载 地址&#xff1a;Index of /qtproject/ 根据电脑系统选择下载linux、macO…

Warm-Flow发布1.7.3 端午节(设计器流和流程图大升级)

Warm-Flow发布1.7.3 端午节&#xff08;设计器流和流程图大升级&#xff09; 更新内容项目介绍功能思维导图演示地址官网Warm-Flow视频 更新内容 [feat] 新版流程图通过前端渲染[perf] 美化流程设计器ui[feat] 办理人权限处理器&#xff0c;新增办理人转换接口&#xff0c;比如…

分布式锁Redisson使用

redission为我们提供了方便使用redis集群的方法&#xff0c;可以使用它完成锁的建立。 依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.36.0</version></dependency>spring引…

Unity UI系统中RectTransform详解

一、基础代码示例 public GameObject node; var rect node.GetComponent<RectTransform>();Debug.Log($"anchoredPosition----{rect.anchoredPosition}"); Debug.Log($"offsetMin.x--{rect.offsetMin}"); Debug.Log($"offsetMax.x--{rect.of…

神经网络(Neural Networks)

设计神经网络的最初动机是编写能够模仿人类大脑学习和思考方式的软件。现今&#xff0c;神经网络也被称为人工神经网络&#xff0c;其工作方式已经与我们所认为的大脑实际工作方式和学习方式大不相同。 研究神经网络的工作始于 20 世纪 50 年代&#xff0c;之后的一段时间它并不…

DeepSeek‑R1-0528 重磅升级:蚂蚁百宝箱免费、无限量调用

DeepSeek‑R1-0528 重磅升级&#xff1a;蚂蚁百宝箱免费、无限量调用 端午假期前一天&#xff0c;DeepSeek‑R1 更新到了 0528 版本&#xff01; 官方说明&#xff1a;0528 版本在深度思考与推理能力方面显著增强——在数学、编程与通用逻辑等多项基准测评中&#xff0c;表现已…

可定制化货代管理系统,适应不同业务模式需求!

在全球化贸易的浪潮下&#xff0c;货运代理行业扮演着至关重要的角色。然而&#xff0c;随着市场竞争的日益激烈&#xff0c;货代企业面临着越来越多的挑战&#xff1a;客户需求多样化、业务流程复杂化、运营成本上升、利润空间压缩……这些挑战迫使货代企业不断寻求创新和突破…

使用基于Xsens惯性传感器的动作捕捉技术测量人体工程学

由于单调和片面的体力消耗&#xff0c;牙科领域的从业者患肌肉骨骼疾病 (MSD) 的几率很高。惯性测量单元 (IMU) 越来越成为评估工作姿势风险的焦点。因此&#xff0c;本研究旨在使用基于惯性传感器的运动捕捉 (MoCap) 评估人体工程学讲座和培训干预对牙科助理学生的姿势风险和M…

设计模式之结构型:桥接模式

桥接模式(Bridge Pattern) 定义 桥接模式是一种​​结构型设计模式​​&#xff0c;通过​​将抽象部分与实现部分分离​​&#xff0c;使它们可以独立变化。它通过组合代替继承&#xff0c;解决多层继承导致的类爆炸问题&#xff0c;适用于​​多维度变化​​的场景(如形状与颜…

Spring Boot 3 整合 MQ 构建聊天消息存储系统

引子 在构建实时聊天服务时&#xff0c;我们既要保证消息的即时传递&#xff0c;又需要对消息进行持久化存储以便查询历史记录。然而&#xff0c;直接同步写入数据库在高并发场景下容易成为性能瓶颈&#xff0c;影响消息的实时性。秉承"没有什么问题是加一层解决不了的&q…

0-EATSA-GNN:基于图节点分类师生机制的边缘感知和两阶段注意力增强图神经网络(code)

code:https://github.com/afofanah/EATSA-GNN. 文章目录 Abstract1. Introduction1.1.动态图场景1.2.EATSA-GNN框架的背景化2. Background2.1.GNN边缘感知挑战2.2.GNN的可解释性问题2.3.EATSA-GNN可解释性3. Related worksAbstract 图神经网络(GNNs)从根本上改变了我们处理和…

解决开发者技能差距:AI 在提升效率与技能培养中的作用

企业在开发者人才方面正面临双重挑战。一方面&#xff0c;IDC 预测&#xff0c;到2025年&#xff0c;全球全职开发者将短缺400万人&#xff1b;另一方面&#xff0c;一些行业巨头已暂停开发者招聘&#xff0c;转而倚重人工智能&#xff08;AI&#xff09;来满足开发需求。这不禁…

内存池学习(一)

一、内存池 1、内存池所使用的内存是什么内存&#xff1f; 指的是虚拟内存&#xff08;堆空间&#xff09;&#xff0c;而不是物理内存 2、为什么会有内存池&#xff1f; 一个系统或者程序长期运行&#xff0c;突然会coredump掉&#xff0c;并且程序又频繁地分配和释放内存…