数据库密码直接明文写在配置中,对安全来说,是一个很大的挑战。一旦密码泄漏,将会带来很大的安全隐患。尤其在一些企业对安全性要求很高,因此我们就考虑如何对密码进行加密。
<dependency>
<groupId>com.github.ulisesbocchiogroupId>
<artifactId>jasypt-spring-boot-starterartifactId>
<version>3.0.4version>
dependency>
jasypt:
encryptor:
algorithm: PBEWithMD5AndDES
# jasypt加密的盐值
password: 123456
iv-generator-classname: org.jasypt.iv.NoIvGenerator
spring:
# 数据库配置
datasource:
# driver-class-name: com.mysql.cj.jdbc.Driver
url: ENC(W+vcCn52uIhTRfwf++D3H1BSjwgAfUvmgzC4ksV5hqjRQrXpFzL5tNKzf6Tpj42kTrlycDr5//wi4jmb+wKrqZGCH2vrcRluP92CH827bJK66qnoUUXVp4UlvC/qYaOFvI/UNWIQUX3SlIUBi5eNA/qS8YWQAxcoBbMQ3qRWTa85il2Ue0vjhF4X7VlNF9LkXXbz9GYhU0g=)
username: ENC(zJI0GE9ZpOJjuaRjk2B9uA==)
password: ENC(a/kikxloS7NP34jJLgu3ng==)
@RestController
public class TestController {
@Value("${spring.datasource.url}")
private String dsUrl;
@GetMapping("/test")
public String getData() {
System.out.println(dsUrl);
return dsUrl;
}
}
jaspcry-spring-boot-starter
的jar包中找到自动装配的入口:spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ulisesbocchio.jasyptspringbootstarter.JasyptSpringBootAutoConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=com.ulisesbocchio.jasyptspringbootstarter.JasyptSpringCloudBootstrapConfiguration
JasyptSpringBootAutoConfiguration
加载配置类EnableEncryptablePropertiesConfiguration
@Configuration
@Import({EnableEncryptablePropertiesConfiguration.class})
public class JasyptSpringBootAutoConfiguration {
public JasyptSpringBootAutoConfiguration() {
}
}
EnableEncryptablePropertiesConfiguration
加载bean工厂后置处理类,EnableEncryptablePropertiesBeanFactoryPostProcessor
@Configuration
@Import({EncryptablePropertyResolverConfiguration.class, CachingConfiguration.class})
@Slf4j
public class EnableEncryptablePropertiesConfiguration {
@Bean
public static EnableEncryptablePropertiesBeanFactoryPostProcessor enableEncryptablePropertySourcesPostProcessor(final ConfigurableEnvironment environment, EncryptablePropertySourceConverter converter) {
return new EnableEncryptablePropertiesBeanFactoryPostProcessor(environment, converter);
}
}
EnableEncryptablePropertiesBeanFactoryPostProcessor#postProcessBeanFactory
,主要是将所有的PropertySource
都转换成了EncryptablePropertySource
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
log.info("Post-processing PropertySource instances");
MutablePropertySources propSources = environment.getPropertySources();
converter.convertPropertySources(propSources);
}
重写了获取配置信息的接口,EncryptablePropertySource#getProperty()
default Object getProperty(EncryptablePropertyResolver resolver, EncryptablePropertyFilter filter, PropertySource<T> source, String name) {
Object value = source.getProperty(name);
if (value != null && filter.shouldInclude(source, name) && value instanceof String) {
String stringValue = String.valueOf(value);
return resolver.resolvePropertyValue(stringValue);
}
return value;
}
DefaultPropertyResolver#resolvePropertyValue
,判断是否加密了,如果加密进行解密操作。
public String resolvePropertyValue(String value) {
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);
}