配置文件隐私信息加密
项目需求
在配置SpringBoot项目时,经常需要配置数据库连接等,难免需要在配置文件里输入不可公开的密码。既然明文显示密码会导致密码泄露,而这些密码又不得不在配置文件里配置,怎么办呢?很自然可以想到,要是能在配置文件里配置加密后的密文,而配置文件在解析时动态解密就好了。
功能实现
jasypt-spring-boot这个组件为我们提供了配置属性加密的支持。本文先介绍如何使用该组件,最后展示其工作原理。
- 项目中导入依赖
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
- 指定加密盐值并获取密文
和其他加密工具一样,用户可以指定一串字符串作为“盐值”,用于个性化加密解密,且保护整个过程。
指定该盐值后,通过运行加密方法,可以直接获得加密后的密文。
这里通过编写请求API完成功能:
@Autowired
private StringEncryptor stringEncryptor;
@GetMapping("/test")
public void encrypt() {
String password = "123456";
System.out.println("加密后的密文为:");
System.out.println(stringEncryptor.encrypt(password));
}
发送请求后控制台输出如下:
- 配置文件中替换明文
如下方式可以将配置文件中的明文替换为该密文:
spring:
datasource:
password: ENC(3WV2dH3igOHqkq5x9S0A7Zrj2TR6tBHdi7Xe/PQlA1CwcRmizJPgSFvrgYcgVZEt)
将密文以ENC()包围,即表示括号里的内容需要先被解密,才可绑定到对应属性。用户也可以指定自己喜欢的前缀后缀来包围密文,但个人感觉ENC()的风格已经很明显,不太必要指定其他前后缀。
- 启动项目
启动项目时,jasypt将密文解密为原本的字符串。解密密文需要使用之前提到的盐值,该盐值同样可以在配置文件中指定:
jasypt:
encryptor:
password: 123456
倘若如此指定,该解密密文又被泄露,同样其他可被该密文解密的其他信息也被泄露。
那就把这个密码也加密吧!喂喂喂,这个盐值是用来解密的,可不能自己加密自己又用自己解密自己。
幸好,SpringBoot提供了多种导入配置属性的功能,可以外部导入、命令行参数导入等。如果在启动命令中以命令行参数形式指定该盐值,其他非系统成员便不可能获得该盐值的明文,具备安全性。
例如,盐值为“123456”,则可设置SpringBoot项目的启动命令为
java -jar -jasypt.encryptor.password=123456 backend.jar
jasypt解密原理
在Spring容器启动后,jasypt会遍历所有配置文件,找到指定前后缀,默认为ENC(),然后提取出其中的密文,调用解密方法得到原字符串并返回。
整个流程涉及两个接口:EncryptablePropertyDetector和EncryptablePropertyResolver。从名字可以看出,前者负责找到所有ENC(),后者解密其中的密文。
源码如下:
/**
* Default property detector that detects encrypted property values with the format "$prefix$encrypted_value$suffix"
* Default values are "ENC(" and ")" respectively.
*
* @author Ulises Bocchio
*/
public class DefaultPropertyDetector implements EncryptablePropertyDetector {
// 默认的前缀和后缀
private String prefix = "ENC(";
private String suffix = ")";
public DefaultPropertyDetector() {
}
public DefaultPropertyDetector(String prefix, String suffix) {
Assert.notNull(prefix, "Prefix can't be null");
Assert.notNull(suffix, "Suffix can't be null");
this.prefix = prefix;
this.suffix = suffix;
}
// 判断配置属性是否是按照jasypt约定规则加密的属性
@Override
public boolean isEncrypted(String property) {
if (property == null) {
return false;
}
final String trimmedValue = property.trim();
return (trimmedValue.startsWith(prefix) &&
trimmedValue.endsWith(suffix));
}
// 去掉默认的前缀和后缀,返回加密的值
@Override
public String unwrapEncryptedValue(String property) {
return property.substring(
prefix.length(),
(property.length() - suffix.length()));
}
}
/**
* @author Ulises Bocchio
*/
public class DefaultPropertyResolver implements EncryptablePropertyResolver {
private final Environment environment;
// 加密和解密的实现
private StringEncryptor encryptor;
// jasypt默认发现器
private EncryptablePropertyDetector detector;
public DefaultPropertyResolver(StringEncryptor encryptor, Environment environment) {
this(encryptor, new DefaultPropertyDetector(), environment);
}
public DefaultPropertyResolver(StringEncryptor encryptor, EncryptablePropertyDetector detector, Environment environment) {
this.environment = environment;
Assert.notNull(encryptor, "String encryptor can't be null");
Assert.notNull(detector, "Encryptable Property detector can't be null");
this.encryptor = encryptor;
this.detector = detector;
}
@Override
public String resolvePropertyValue(String value) {
// 该方法获取加密的属性,然后使用StringEncryptor解密并返回
return Optional.ofNullable(value)
.map(environment::resolvePlaceholders)
.filter(detector::isEncrypted) // 过滤加密属性
.map(resolvedValue -> {
try {
// 去除前缀和后缀获取真正加密的值
String unwrappedProperty = detector.unwrapEncryptedValue(resolvedValue.trim());
String resolvedProperty = environment.resolvePlaceholders(unwrappedProperty);
// 解密获得明文
return encryptor.decrypt(resolvedProperty);
} catch (EncryptionOperationNotPossibleException e) {
throw new DecryptionException("Unable to decrypt property: " + value + " resolved to: " + resolvedValue + ". Decryption of Properties failed, make sure encryption/decryption " +
"passwords match", e);
}
})
.orElse(value);
}
}
可以看到,jasypt提供了两个默认的实现类供使用,这也就意味着用户可以用自己的实现类替换两者,实现自己的加密解密功能。
参考博客
https://blog.csdn.net/qq_32238611/article/details/122724605
贡献者:李治圻-后端