如何实现拦截Druid数据源自动解密注入的帐密操作详细解析?
- 内容介绍
- 文章标签
- 相关推荐
本文共计2050个文字,预计阅读时间需要9分钟。
目录 + 背景介绍 + 加密数据源实现流程 + 基础加固 + 额外测试 + 启动记录 + 背景介绍 + SpringBoot + 项目,使用Druid + 自动装配的数据源,数据源的账号密码配置加密后,如何完成数据源的装配?
1. 在SpringBoot项目中,首先需要在`application.properties`或`application.yml`中配置Druid数据源的基本信息。
2.为数据源的账号密码添加加密配置,可以使用Java的加密工具,如AES。
3.在配置文件中,使用加密后的密码替换明文密码。
4.在SpringBoot启动类或配置类中,使用`@Configuration`注解创建一个数据源配置类。
5.在配置类中,通过`@Bean`注解创建一个`DataSource`的Bean,并指定使用加密后的密码。
6.配置Druid的监控功能,以便于查看数据库连接池的状态。
示例代码:
java
import com.alibaba.druid.pool.DruidDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;import java.util.Base64;@Configurationpublic class DataSourceConfig {
@Bean public DruidDataSource dataSource() throws Exception { DruidDataSource dataSource=new DruidDataSource(); // 数据库连接信息 dataSource.setDriverClassName(com.mysql.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/db_name); // 加密后的密码 String encryptedPassword=encryptPassword(your_password); dataSource.setUsername(your_username); dataSource.setPassword(encryptedPassword);
// 其他配置... return dataSource; }
private String encryptPassword(String password) throws Exception { // 生成密钥 KeyGenerator keyGenerator=KeyGenerator.getInstance(AES); keyGenerator.init(128, new SecureRandom()); SecretKey secretKey=keyGenerator.generateKey(); byte[] keyBytes=secretKey.getEncoded(); SecretKeySpec secretKeySpec=new SecretKeySpec(keyBytes, AES);
// 加密密码 Cipher cipher=Cipher.getInstance(AES); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] encryptedBytes=cipher.doFinal(password.getBytes()); return Base64.getEncoder().encodeToString(encryptedBytes); }}
这样,当SpringBoot应用启动时,会自动装配并使用加密后的数据源。
目录
- 背景
- 加密数据源自主实现流程
- 基础巩固
- 额外尝试
- 启示录
背景
SpringBoot 项目,使用 Druid 自动装配的数据源,数据源的帐号密码配置加密后,如何完成数据源的装配呢?
druid-spring-boot-starter 虽然自带了加密配置,但是密钥也是配置的,如果需要用自定义的加密解密工具,如果不用自带的工具,怎么自定义实现加密数据源的装配呢?
本文从 DruidDataSourceAutoConfigure 类源码入手,仿造该类,自定义一个数据源注入配置,在真正注入 DruidDataSource 之前,对 druid 配置信息完成解密。
主要思考三个问题:
- 自定的
Configuration类中的@Bean注入一个DruidDataSource,为什么比自动装配的时机早呢? - 如果自定义一个自动装配类, 包含
DataSourceProperties属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure类之前装配,怎么实现呢? - 自动装配类的工作原理是什么?注入优先级怎么确定的?
加密数据源自主实现流程
Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan
@ConfigurationProperties 用法限制,我想到一个解决办法,为当前类加上 @Component,同时制定一个不可能的注入条件:@ConditionalOnProperty(prefix = "xx",name = "xxx", havingValue = "impossible")。
不用官方的加密插件,自定义 Druid 的解密配置,我想到的方法是完全仿照 Druid 数据源的自动装配过程,改写 DruidDataSource 的注入过程。
关键是修改 DataSourceProperties 这个类的实例的帐号密码属性,其他完全照搬 DruidDataSourceAutoConfigure 实现即可。
第一步,由于 Druid 自动注入的数据源 DruidDataSourceWrapper 是一个包内类,不能直接拿来用,所以完全拷贝一份这个类,定义为咱们自己的数据源类:
@Component @ConfigurationProperties("spring.datasource.druid") @ConditionalOnProperty(prefix = "spring.datasource",name = "encrypted", havingValue = "impossible") public class MyEncryptedDatasourceWrapper extends DruidDataSource implements InitializingBean { @Autowired private DataSourceProperties basicProperties; public MyEncryptedDatasourceWrapper() { } @Override public void afterPropertiesSet() { if (super.getUsername() == null) { super.setUsername(this.basicProperties.determineUsername()); } if (super.getPassword() == null) { super.setPassword(this.basicProperties.determinePassword()); } if (super.getUrl() == null) { super.setUrl(this.basicProperties.determineUrl()); } if (super.getDriverClassName() == null) { super.setDriverClassName(this.basicProperties.getDriverClassName()); } } @Autowired( required = false ) public void autoAddFilters(List<Filter> filters) { super.filters.addAll(filters); } @Override public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) { try { super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); } catch (IllegalArgumentException var4) { super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis; } } }
第二步,自定义一个 DruidDataSourceAutoConfigure 类,内容与该类一样,但是多一个数据源配置属性:
@Configuration @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class MyEncryptedDatasourceWrapperConfig { /** * 该属性封装了 spring.datasource 属性,需要对它的帐号、密码属性进行解密 */ @Autowired private DataSourceProperties basicProperties; /** * 使用数据源配置信息,解密帐号和密码后创建数据库连接池 * @return */ @Bean public DataSource dataSource() { // TODO 对密码解密并设置回去 basicProperties.setPassword(password); return new MyEncryptedDatasourceWrapper(); } }
这样就完成了 Spring druid 数据源配置的解密处理了。
基础巩固
boolean proxyBeanMethods() 默认值是 true. 从这个成员变量的注释中,我们可以看到一句话 Specify whether {@code @Bean} methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct {@code @Bean} method calls in user code.
其实从这句话,我们就可以初步得到我们想要的答案了:在带有 @Configuration 注解的类中,一个带有 @Bean 注解的方法显式调用另一个带有 @Bean 注解的方法,返回的是共享的单例对象。
参考文档:《Component 和 Configuration 区别》
额外尝试
加密数据源配置的解密流程,核心在 DataSourceProperties 这个实例装配完成后修改密码信息,尝试自定义一个 @Configuration 中 @Bean 注入一个 DataSourceProperties 实例,但是这个对象到了 Druid 自动注入类那里,属性还是没有发生变化:
@Configuration @EnableConfigurationProperties(DataSourceProperties.class) public class MyAutoConfig { @Autowired private DataSourceProperties basicProperties; public MyAutoConfig() { System.out.println("My Auto config"); } @Bean public DataSourceProperties basicProperties() { // TODO 解密配置 System.out.println("username " + basicProperties.getUsername()); return basicProperties; } }
这里 @Bean 注入生效了,到了 DruidDataSource 那使用的也是这个实例,单步调试对象地址是一样的,但是改的属性没有生效。
数据源实例化配置时引用的属性:
但是两个地方的属性却不同,前一步解密的信息并没有传递到真正使用的地方。 理论上,同一个对象,前面修改了属性,这里同一个线程里面,属性应该变化了才对呢!不得其解。
启示录
回顾开头的三个问题:
- 自定的
Configuration类中的@Bean注入一个DruidDataSource,为什么比自动装配的时机早呢?因为@Bean属于当前项目扫描路径,它里面的类注入优先级高于第三方 jar 包中的spring.factories的装配类。 - 如果自定义一个自动装配类, 包含
DataSourceProperties属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure类之前装配,怎么实现呢?尝试定义一个装配类 @Bean 装配一个数据源DataSourceProperties对象,并修改配置。 - 自动装配类的工作原理是什么?注入优先级怎么确定的?
spring.factories的本质是 SPI,它针对的是第三方 jar 包,不需要手动配置扫描路径,又需要自动注入的情况,是各种 starter 定义底层实现途径。
优先级:本地扫描路径的 Configuration -> 实现了 BeanDefinitionRegistryPostProcessor 接口的类 -> spring.factories 中的自动装配类。
以上就是拦截Druid数据源自动注入帐密解密实现详解的详细内容,更多关于Druid注入帐密解密拦截的资料请关注自由互联其它相关文章!
本文共计2050个文字,预计阅读时间需要9分钟。
目录 + 背景介绍 + 加密数据源实现流程 + 基础加固 + 额外测试 + 启动记录 + 背景介绍 + SpringBoot + 项目,使用Druid + 自动装配的数据源,数据源的账号密码配置加密后,如何完成数据源的装配?
1. 在SpringBoot项目中,首先需要在`application.properties`或`application.yml`中配置Druid数据源的基本信息。
2.为数据源的账号密码添加加密配置,可以使用Java的加密工具,如AES。
3.在配置文件中,使用加密后的密码替换明文密码。
4.在SpringBoot启动类或配置类中,使用`@Configuration`注解创建一个数据源配置类。
5.在配置类中,通过`@Bean`注解创建一个`DataSource`的Bean,并指定使用加密后的密码。
6.配置Druid的监控功能,以便于查看数据库连接池的状态。
示例代码:
java
import com.alibaba.druid.pool.DruidDataSource;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;import java.util.Base64;@Configurationpublic class DataSourceConfig {
@Bean public DruidDataSource dataSource() throws Exception { DruidDataSource dataSource=new DruidDataSource(); // 数据库连接信息 dataSource.setDriverClassName(com.mysql.jdbc.Driver); dataSource.setUrl(jdbc:mysql://localhost:3306/db_name); // 加密后的密码 String encryptedPassword=encryptPassword(your_password); dataSource.setUsername(your_username); dataSource.setPassword(encryptedPassword);
// 其他配置... return dataSource; }
private String encryptPassword(String password) throws Exception { // 生成密钥 KeyGenerator keyGenerator=KeyGenerator.getInstance(AES); keyGenerator.init(128, new SecureRandom()); SecretKey secretKey=keyGenerator.generateKey(); byte[] keyBytes=secretKey.getEncoded(); SecretKeySpec secretKeySpec=new SecretKeySpec(keyBytes, AES);
// 加密密码 Cipher cipher=Cipher.getInstance(AES); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] encryptedBytes=cipher.doFinal(password.getBytes()); return Base64.getEncoder().encodeToString(encryptedBytes); }}
这样,当SpringBoot应用启动时,会自动装配并使用加密后的数据源。
目录
- 背景
- 加密数据源自主实现流程
- 基础巩固
- 额外尝试
- 启示录
背景
SpringBoot 项目,使用 Druid 自动装配的数据源,数据源的帐号密码配置加密后,如何完成数据源的装配呢?
druid-spring-boot-starter 虽然自带了加密配置,但是密钥也是配置的,如果需要用自定义的加密解密工具,如果不用自带的工具,怎么自定义实现加密数据源的装配呢?
本文从 DruidDataSourceAutoConfigure 类源码入手,仿造该类,自定义一个数据源注入配置,在真正注入 DruidDataSource 之前,对 druid 配置信息完成解密。
主要思考三个问题:
- 自定的
Configuration类中的@Bean注入一个DruidDataSource,为什么比自动装配的时机早呢? - 如果自定义一个自动装配类, 包含
DataSourceProperties属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure类之前装配,怎么实现呢? - 自动装配类的工作原理是什么?注入优先级怎么确定的?
加密数据源自主实现流程
Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan
@ConfigurationProperties 用法限制,我想到一个解决办法,为当前类加上 @Component,同时制定一个不可能的注入条件:@ConditionalOnProperty(prefix = "xx",name = "xxx", havingValue = "impossible")。
不用官方的加密插件,自定义 Druid 的解密配置,我想到的方法是完全仿照 Druid 数据源的自动装配过程,改写 DruidDataSource 的注入过程。
关键是修改 DataSourceProperties 这个类的实例的帐号密码属性,其他完全照搬 DruidDataSourceAutoConfigure 实现即可。
第一步,由于 Druid 自动注入的数据源 DruidDataSourceWrapper 是一个包内类,不能直接拿来用,所以完全拷贝一份这个类,定义为咱们自己的数据源类:
@Component @ConfigurationProperties("spring.datasource.druid") @ConditionalOnProperty(prefix = "spring.datasource",name = "encrypted", havingValue = "impossible") public class MyEncryptedDatasourceWrapper extends DruidDataSource implements InitializingBean { @Autowired private DataSourceProperties basicProperties; public MyEncryptedDatasourceWrapper() { } @Override public void afterPropertiesSet() { if (super.getUsername() == null) { super.setUsername(this.basicProperties.determineUsername()); } if (super.getPassword() == null) { super.setPassword(this.basicProperties.determinePassword()); } if (super.getUrl() == null) { super.setUrl(this.basicProperties.determineUrl()); } if (super.getDriverClassName() == null) { super.setDriverClassName(this.basicProperties.getDriverClassName()); } } @Autowired( required = false ) public void autoAddFilters(List<Filter> filters) { super.filters.addAll(filters); } @Override public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) { try { super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); } catch (IllegalArgumentException var4) { super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis; } } }
第二步,自定义一个 DruidDataSourceAutoConfigure 类,内容与该类一样,但是多一个数据源配置属性:
@Configuration @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class MyEncryptedDatasourceWrapperConfig { /** * 该属性封装了 spring.datasource 属性,需要对它的帐号、密码属性进行解密 */ @Autowired private DataSourceProperties basicProperties; /** * 使用数据源配置信息,解密帐号和密码后创建数据库连接池 * @return */ @Bean public DataSource dataSource() { // TODO 对密码解密并设置回去 basicProperties.setPassword(password); return new MyEncryptedDatasourceWrapper(); } }
这样就完成了 Spring druid 数据源配置的解密处理了。
基础巩固
boolean proxyBeanMethods() 默认值是 true. 从这个成员变量的注释中,我们可以看到一句话 Specify whether {@code @Bean} methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct {@code @Bean} method calls in user code.
其实从这句话,我们就可以初步得到我们想要的答案了:在带有 @Configuration 注解的类中,一个带有 @Bean 注解的方法显式调用另一个带有 @Bean 注解的方法,返回的是共享的单例对象。
参考文档:《Component 和 Configuration 区别》
额外尝试
加密数据源配置的解密流程,核心在 DataSourceProperties 这个实例装配完成后修改密码信息,尝试自定义一个 @Configuration 中 @Bean 注入一个 DataSourceProperties 实例,但是这个对象到了 Druid 自动注入类那里,属性还是没有发生变化:
@Configuration @EnableConfigurationProperties(DataSourceProperties.class) public class MyAutoConfig { @Autowired private DataSourceProperties basicProperties; public MyAutoConfig() { System.out.println("My Auto config"); } @Bean public DataSourceProperties basicProperties() { // TODO 解密配置 System.out.println("username " + basicProperties.getUsername()); return basicProperties; } }
这里 @Bean 注入生效了,到了 DruidDataSource 那使用的也是这个实例,单步调试对象地址是一样的,但是改的属性没有生效。
数据源实例化配置时引用的属性:
但是两个地方的属性却不同,前一步解密的信息并没有传递到真正使用的地方。 理论上,同一个对象,前面修改了属性,这里同一个线程里面,属性应该变化了才对呢!不得其解。
启示录
回顾开头的三个问题:
- 自定的
Configuration类中的@Bean注入一个DruidDataSource,为什么比自动装配的时机早呢?因为@Bean属于当前项目扫描路径,它里面的类注入优先级高于第三方 jar 包中的spring.factories的装配类。 - 如果自定义一个自动装配类, 包含
DataSourceProperties属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure类之前装配,怎么实现呢?尝试定义一个装配类 @Bean 装配一个数据源DataSourceProperties对象,并修改配置。 - 自动装配类的工作原理是什么?注入优先级怎么确定的?
spring.factories的本质是 SPI,它针对的是第三方 jar 包,不需要手动配置扫描路径,又需要自动注入的情况,是各种 starter 定义底层实现途径。
优先级:本地扫描路径的 Configuration -> 实现了 BeanDefinitionRegistryPostProcessor 接口的类 -> spring.factories 中的自动装配类。
以上就是拦截Druid数据源自动注入帐密解密实现详解的详细内容,更多关于Druid注入帐密解密拦截的资料请关注自由互联其它相关文章!

