当前位置: 首页 > article >正文

Spring 配置文件敏感数据加密

需求:
配置文件中会有一些敏感数据,例如数据库账号密码,需要非明文存储。
实现方案:

  • jasypt : 最常见的方案
  • 自定义加密/解密:Spring 的 EnvironmentPostProcessor
  • KMS(密钥管理服务):像 AWS KMS、阿里云 KMS 这样的密钥管理服务可以用来加密和解密敏感数据。

项目Demo :我的Demo工程

一、jasypt

  1. 添加依赖
  <dependency>
       <groupId>com.github.ulisesbocchio</groupId>
       <artifactId>jasypt-spring-boot-starter</artifactId>
       <version>3.0.4</version>
   </dependency>
  1. 配置文件
jasypt.encryptor.password=your-strong-password
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=myuser
spring.datasource.password=ENC(Gs4gAe89RnF6z5yJiQ==)
  1. 加密/解密工具类
public class JasyptUtil {

    public static String encrypt(String text, String password) {
        BasicTextEncryptor encryptor = new BasicTextEncryptor();
        encryptor.setPassword(password);
        return encryptor.encrypt(text);
    }

    public static String decrypt(String encryptedText, String password) {
        BasicTextEncryptor encryptor = new BasicTextEncryptor();
        encryptor.setPassword(password);
        return encryptor.decrypt(encryptedText);
    }

    public static String encrypt(String text, String password, String algorithm) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(password);
        encryptor.setAlgorithm(algorithm);
        return encryptor.encrypt(text);
    }

    public static String decrypt(String encryptedText, String password, String algorithm) {
        StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
        encryptor.setPassword(password);
        encryptor.setAlgorithm(algorithm);
        return encryptor.decrypt(encryptedText);
    }
}

二、EnvironmentPostProcessor

EnvironmentPostProcessor 是 Spring Boot 提供的一个扩展接口,用于在 Spring Environment 对象完成配置属性加载后,且 Spring 容器创建前,对 Environment 中的属性进行修改或添加。它常用于在应用启动时动态配置环境变量,或在不直接修改配置文件的前提下实现敏感数据解密、配置调整等功能

  1. 自定义实现
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.origin.OriginTrackedValue;
import org.springframework.core.env.*;
import org.springframework.util.StringUtils;

public class EncryptEnvironmentPostProcessor implements EnvironmentPostProcessor {
    private static final String ENC_PREFIX = "ENC(";
    private static final String ENC_SUFFIX = ")";

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        MutablePropertySources propertySources = environment.getPropertySources();

        // 遍历每个 PropertySource
        for (PropertySource<?> propertySource : propertySources) {
            String name = propertySource.getName();
            if (propertySource instanceof MapPropertySource
                && (name.contains("application-") || name.contains("application."))) {
                Map<String, Object> source = (Map<String, Object>) propertySource.getSource();
                Map<String, Object> decryptedProperties = new HashMap<>();

                for (Map.Entry<String, Object> entry : source.entrySet()) {
                    String key = entry.getKey();
                    Object value = entry.getValue();
                    // 使用 toString() 方法而不是直接强制转换
                    String stringValue = value instanceof OriginTrackedValue
                            ? value.toString()
                            : (value instanceof String ? (String) value : null);
                    if (stringValue != null) {
                        // 检查是否有 ENC() 标记
                        if (StringUtils.hasText(stringValue) && stringValue.startsWith(ENC_PREFIX) && stringValue.endsWith(ENC_SUFFIX)) {
                            String encryptedValue = stringValue.substring(ENC_PREFIX.length(), stringValue.length() - ENC_SUFFIX.length());
                            String decryptedValue = decrypt(encryptedValue);
                            System.out.println("==========encryptedValue:" + encryptedValue + ";decryptedValue=" + decryptedValue);
                            decryptedProperties.put(key, decryptedValue);
                        } else {
                            decryptedProperties.put(key, stringValue); // 保留未加密的值
                        }
                    } else if (value instanceof String[]) {
                        // 处理数组情况
                        String[] values = (String[]) value;
                        for (int i = 0; i < values.length; i++) {
                            String valueStr = values[i].toString(); // 确保调用 toString()
                            if (StringUtils.hasText(valueStr) && valueStr.startsWith(ENC_PREFIX) && valueStr.endsWith(ENC_SUFFIX)) {
                                String encryptedValue = valueStr.substring(ENC_PREFIX.length(), valueStr.length() - ENC_SUFFIX.length());
                                String decryptedValue = decrypt(encryptedValue);
                                values[i] = decryptedValue;
                            }
                        }
                        decryptedProperties.put(key, values);
                    }
                }
                // 要注意 替换原来的 而不是添加到first or last ,否则 可能会有问题
                propertySources.replace(name, new MapPropertySource(name, decryptedProperties));
            }
        }
    }

    private String decrypt(String encryptedText) {
        // 替换成实际的解密逻辑
        return AESUtil.decrypt(encryptedText, "IGJ+xzgyBYMoQQJ6YYa6OQ==");
    }
}
  1. 配置生效

要使 EnvironmentPostProcessor 生效,通常需要在 META-INF/spring.factories 文件中注册它:
org.springframework.boot.env.EnvironmentPostProcessor=com.example.CustomEnvironmentPostProcessor

META-INF/spring.factories 文件是 Spring 框架中用于自动配置和组件扫描的重要机制之一。它主要用于定义自动配置类和初始化器类,使得 Spring Boot 能够自动发现并加载这些类。

要注意的点

  • EnvironmentPostProcessor 在 ApplicationContext 初始化之前执行,因此在这个阶段你还不能访问到 ApplicationContext 中的 bean。
  • MutablePropertySources 是 Spring 框架中用于管理多个 PropertySource 对象的容器。每个 PropertySource 都代表一个配置源,例如属性文件、系统环境变量、JVM 系统属性等。
  • MapPropertySource.getSource() 返回的 Map 是不可修改的 UnmodifiableMap。
  • Spring Boot 加载配置文件的过程是基于 Environment 和 PropertySource 的概念来实现的,配置文件可以来自多个位置,并且支持不同优先级。所以替换后新的 MapPropertySource 要注意顺序,一般用 repalce ,谨慎使用 addFirstaddLastaddBefore等。
  • 如果有多个 EnvironmentPostProcessor 实现,它们会按照 @Order 注解的顺序执行。如果没有指定 @Order,则按类名的字母顺序执行。

我的应用配置加载顺序参考:

configurationProperties
servletConfigInitParams
servletContextInitParams
systemProperties
systemEnvironment
random
Config resource 'class path resource [application-dal.properties]' via location 'classpath:application-dal.properties'
Config resource 'class path resource [application-dev.properties]' via location 'optional:classpath:/'
Config resource 'class path resource [application.properties]' via location 'optional:classpath:/'

http://www.kler.cn/a/375828.html

相关文章:

  • 【数据结构】宜宾大学-计院-实验六
  • 从0到1搭建flink程序-WordCount(图文/详细/mac)
  • img 标签的 object-fit 属性
  • npm入门教程6:npm脚本
  • 基于Spring Boot的员工与部门信息管理系统:增删改查全攻略
  • 高并发-负载均衡
  • SQLite 语法
  • 【React】配置图标和题目
  • 【系统设计】深入了解四种通信机制:从同步到异步的演变
  • 【一网打尽】前端Vue和React项目的构建
  • 安装 Visual Studio
  • PPT素材、模板免费下载!
  • Qt 使用QXlsx将QTableView数据导出到Excel表格
  • Ceph 学习指南 集群部署【 cephadm 】
  • 嵌入式硬件设计:智能设备的核心
  • 人工智能中的学习方法详解
  • 盖电子章的软件
  • Tomcat安装和配置(超详细)
  • [运维] 服务器本地网络可用性检查脚本
  • 【深度学习】实验 — 动手实现 GPT【三】:LLM架构、LayerNorm、GELU激活函数
  • 基于单片机的宠物自动喂食系统的设计
  • 【Unity实战笔记】第二十二 · 基于SMB的角色控制中遇到的一些问题(斜坡移动鬼畜、落地卡顿、角色突进、头发动画失效等)
  • (五)关于InternVL2的模型训练二(如何训练目标定位模型)
  • IDEA中通义灵码的使用技巧
  • Python 游戏开发库比较与示例
  • vue当中的$使用方法