大家在日常工作中是否有对配置加密的要求呢?
可能你一下子就会想到,Jasypt作为标准的解决方案。不过,你有没有想过先手动搓一个呢?
本文我们先来看看,如何手动写一个加密解密配置的机制。
需求描述
对配置文件中的数据库连接配置,执行加密。确保上传到git仓库的配置以密文形式存在。
密钥不会写到配置文件,可以通过命令行参数或者环境变量的方式注入。保证安全性。
方案原理是:手动实现配置加解密+自定义DataSource Bean完成解密。
本场景针对数据库连接配置进行加密解密。当然,其他的加密配置项也适用。
准备
- 数据库实例,demo项目使用的是postgresql。
- 对称加密工具类,可以实现加密解密。
- 自定义DataSource Bean配置类,在此配置类中实现对用户名和密码的解密后,再构造DataSource对象。
java
/**
* 自定义数据源配置类
* 支持从命令行参数或环境变量读取解密密钥,解密数据库用户名和密码后构造 DataSource
*/
@Configuration
public class DataSourceSimpleConfig {
private static final Logger logger = LoggerFactory.getLogger(DataSourceSimpleConfig.class);
private static final String ENC_PREFIX = "{enc}";
// 密钥获取优先级:命令行参数 > 环境变量
@Value("${db.encrypt.key:#{systemEnvironment['ENCRYPT_KEY']}}")
private String encryptKey;
private final Environment environment;
public DataSourceSimpleConfig(Environment environment) {
this.environment = environment;
}
/**
* 自定义 DataSource Bean
* 解密数据库配置后构造 HikariDataSource
*/
@Bean
public DataSource dataSource() throws Exception {
logger.info("========== 开始配置数据源 ==========");
// 获取原始配置
String url = environment.getProperty("spring.datasource.url");
String encryptedUsername = environment.getProperty("spring.datasource.username");
String encryptedPassword = environment.getProperty("spring.datasource.password");
String driverClassName = environment.getProperty("spring.datasource.driver-class-name");
// 检查是否需要解密
String username;
String password;
if (encryptedUsername.startsWith(ENC_PREFIX)) {
// 需要解密用户名
logger.info("检测到加密的用户名,开始解密...");
String encryptedUser = encryptedUsername.substring(ENC_PREFIX.length());
username = decrypt(encryptedUser, encryptKey);
logger.info("用户名解密成功");
} else {
username = encryptedUsername;
logger.info("用户名未加密,直接使用");
}
if (encryptedPassword.startsWith(ENC_PREFIX)) {
// 需要解密密码
logger.info("检测到加密的密码,开始解密...");
String encryptedPass = encryptedPassword.substring(ENC_PREFIX.length());
password = decrypt(encryptedPass, encryptKey);
logger.info("密码解密成功");
} else {
password = encryptedPassword;
logger.info("密码未加密,直接使用");
}
// 创建 HikariConfig
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(url);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);
if (driverClassName != null && !driverClassName.isEmpty()) {
hikariConfig.setDriverClassName(driverClassName);
}
// 创建数据源
HikariDataSource dataSource = new HikariDataSource(hikariConfig);
logger.info("✅ 数据源配置完成");
logger.info(" JDBC URL: {}", url);
logger.info("========== 数据源配置完成 ==========");
return dataSource;
}
/**
* 解密方法
*
* @param encryptedText 加密文本(Base64 编码)
* @param key 解密密钥(Base64 编码)
* @return 解密后的明文
*/
private String decrypt(String encryptedText, String key) throws Exception {
try {
return AesUtil.decryptGCM(encryptedText, key);
} catch (Exception e) {
logger.error("❌ 解密失败!", e);
logger.error(" 错误信息: {}", e.getMessage());
throw new RuntimeException("解密失败,请检查密钥是否正确", e);
}
}
}
- 准备密钥,可以随便写。密钥通过环境变量配置。
步骤
- 首先创建空白项目,配置完数据库连接后,启动项目并确认数据库连接正常。
- 根据生成的密钥,创建用户名密码的密文。并以特定的格式替换配置中原来的位置。
vbnet
url: jdbc:postgresql://
username: "{enc}111111"
password: "{enc}222222"
- 添加上一节准备好的
DataSourceSimpleConfig类。 - 在启动配置中,添加密钥环境变量配置
ENCRYPT_KEY="333333" - 启动测试,可以看到仍然实现了正常连接。并且,对用户名和密码的解密日志也打印出来了
erlang
========== 开始配置数据源 ==========
检测到加密的用户名,开始解密...
已获取解密密钥(长度 4)
用户名解密成功
检测到加密的密码,开始解密...
已获取解密密钥(长度 4)
密码解密成功
测试环境
- JDK:17
- Spring boot:4.0.6
版本仅供参考,无强制要求。
总结
怎么样,是不是很简单!
经过这么一番改造,上传到git中的只有加密后的字符串。密钥独立配置提升了安全性。
对于当前示例这个方案足够了。
但是,如果很多配置项都需要加密呢?这么一个一个写,岂不是要累死?
解决方案就是拿数据库当配置中心来用,将配置存储到数据库里就能避免传入git了。所以,本文首先拿数据库连接加密来试水。
下篇,我们来探讨,如何将数据库作为配置中心来使用,并在启动时自动加载数据库配置,敬请期待!