java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > SpringBoot敏感信息加解密

SpringBoot中配置文件敏感信息加密解密的实现方案详解

作者:代码怪兽大作战

在现代企业级应用中,application.yml 或 application.properties 常用于配置数据库等中间件的连接信息,但将明文密码直接写入配置文件中存在诸多风险,下面我们就来看看如何对配置文件敏感信息加密解密吧

背景与挑战 

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

spring:
  datasource:
    username: myuser
    password: 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"
    );

    @Override
    public 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_user
    password: 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 配置体系的自然兼容与开发便利性。建议结合项目实际,进一步扩展支持密钥动态更新、配置加密校验等高级特性。

到此这篇关于SpringBoot中配置文件敏感信息加密解密的实现方案详解的文章就介绍到这了,更多相关SpringBoot敏感信息加解密内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文