springboot数据库密码加密

方案一

SpringBoot在配置文件application中配置的关于数据库连接信息,在实际使用时是转换为DataSource类,那么只要将SpringBoot实现的DataSource继承类中, 将实际密文解密即可。通过查看源码可得知,SpringBootDataSource实现类为HikariDataSource,那么我们通过BeanPostProcessor在实例化HikariDataSource时, 替换密文即可。代码如下

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/app
username: root
password: cm9vdA==
driver-class-name: com.mysql.jdbc.Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
@Bean
public static BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof HikariDataSource) {
HikariDataSource hikariDataSource = (HikariDataSource) bean;
hikariDataSource.setPassword(new String(Base64Utils.decode(hikariDataSource.getPassword().getBytes())));
}
return null;
}
};
}

方案二

使用 jasypt

1
2
3
4
5
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>

开启

1
2
3
@EnableEncryptableProperties
public class SpringbootApplication {
}

自定义处理器解密处理器

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/app
username: root
password: "{cipher}cm9vdA"
driver-class-name: com.mysql.cj.jdbc.Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

public static final String ENCODED_PASSWORD_HINT = "{cipher}";

@Bean
public static EncryptablePropertyDetector encryptablePropertyDetector() {
return new EncryptablePropertyDetector() {

@Override
public boolean isEncrypted(String s) {
if (null != s) {
return s.startsWith(ENCODED_PASSWORD_HINT);
}
return false;
}

@Override
public String unwrapEncryptedValue(String s) {
return s.substring(ENCODED_PASSWORD_HINT.length());
}
};
}

@Bean
public static EncryptablePropertyResolver encryptablePropertyResolver() {
return new EncryptablePropertyResolver() {
@Override
public String resolvePropertyValue(String s) {
if (null != s && s.startsWith(ENCODED_PASSWORD_HINT)) {
return new String(Base64Utils.decode(s.substring(ENCODED_PASSWORD_HINT.length()).getBytes()));
}
return s;
}
};
}

方案三

根据方案二的实现原理,使用BeanFactoryPostProcessor实现一个自动解密配置文件的处理器

1
2
3
4
5
6
spring:
datasource:
url: jdbc:mysql://localhost:3306/app
username: root
password: "{cipher}cm9vdA"
driver-class-name: com.mysql.cj.jdbc.Driver
1
2
3
4
@Bean
public static EncryptationAwarePropertyPlaceholderConfigurer enableEncryptablePropertySourcesPostProcessor(ConfigurableEnvironment environment) {
return new DecodeBeanFactoryPostProcessor(environment);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class DecodeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {


private ConfigurableEnvironment environment;

public DecodeBeanFactoryPostProcessor(ConfigurableEnvironment environment) {
this.environment = environment;
}


@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
MutablePropertySources propertySources = environment.getPropertySources();
StreamSupport.stream(propertySources.spliterator(), false).forEach(ps -> {
//示例代码,仅仅处理application.yml相关的解码操作
if (ps.getName().equals("applicationConfig: [classpath:/application.yml]")) {
Map<Object, Object> source = (Map) ps.getSource();
source.keySet().forEach(k -> {
Object value = source.computeIfPresent(k, (key, v) -> {
String cipher = v.toString();
if (cipher.startsWith("{cipher}")) {
return new String(Base64Utils.decode(cipher.substring("{cipher}".length()).getBytes()));
}
return v;
});
});
}
});
}

其中需要注意的是,DecodeBeanFactoryPostProcessor的实例化需要在ApplicationContext加载成功后再去实例化,确保ConfigurableEnvironment已被正确初始化