攔截Druid數(shù)據(jù)源自動(dòng)注入帳密解密實(shí)現(xiàn)詳解
背景
SpringBoot 項(xiàng)目,使用 Druid 自動(dòng)裝配的數(shù)據(jù)源,數(shù)據(jù)源的帳號(hào)密碼配置加密后,如何完成數(shù)據(jù)源的裝配呢?
druid-spring-boot-starter
雖然自帶了加密配置,但是密鑰也是配置的,如果需要用自定義的加密解密工具,如果不用自帶的工具,怎么自定義實(shí)現(xiàn)加密數(shù)據(jù)源的裝配呢?
本文從 DruidDataSourceAutoConfigure
類源碼入手,仿造該類,自定義一個(gè)數(shù)據(jù)源注入配置,在真正注入 DruidDataSource
之前,對(duì) druid 配置信息完成解密。
主要思考三個(gè)問題:
- 自定的
Configuration
類中的@Bean
注入一個(gè)DruidDataSource
,為什么比自動(dòng)裝配的時(shí)機(jī)早呢? - 如果自定義一個(gè)自動(dòng)裝配類, 包含
DataSourceProperties
屬性,對(duì)它的帳號(hào)密碼解密后,讓它在DruidDataSourceAutoConfigure
類之前裝配,怎么實(shí)現(xiàn)呢? - 自動(dòng)裝配類的工作原理是什么?注入優(yōu)先級(jí)怎么確定的?
加密數(shù)據(jù)源自主實(shí)現(xiàn)流程
Not registered via @EnableConfigurationProperties, marked as Spring component, or scanned via @ConfigurationPropertiesScan
@ConfigurationProperties
用法限制,我想到一個(gè)解決辦法,為當(dāng)前類加上 @Component
,同時(shí)制定一個(gè)不可能的注入條件:@ConditionalOnProperty(prefix = "xx",name = "xxx", havingValue = "impossible")
。
不用官方的加密插件,自定義 Druid 的解密配置,我想到的方法是完全仿照 Druid 數(shù)據(jù)源的自動(dòng)裝配過程,改寫 DruidDataSource
的注入過程。
關(guān)鍵是修改 DataSourceProperties 這個(gè)類的實(shí)例的帳號(hào)密碼屬性,其他完全照搬 DruidDataSourceAutoConfigure
實(shí)現(xiàn)即可。
第一步,由于 Druid 自動(dòng)注入的數(shù)據(jù)源 DruidDataSourceWrapper
是一個(gè)包內(nèi)類,不能直接拿來用,所以完全拷貝一份這個(gè)類,定義為咱們自己的數(shù)據(jù)源類:
@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; } } }
第二步,自定義一個(gè) DruidDataSourceAutoConfigure
類,內(nèi)容與該類一樣,但是多一個(gè)數(shù)據(jù)源配置屬性:
@Configuration @EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class}) @Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class}) public class MyEncryptedDatasourceWrapperConfig { /** * 該屬性封裝了 spring.datasource 屬性,需要對(duì)它的帳號(hào)、密碼屬性進(jìn)行解密 */ @Autowired private DataSourceProperties basicProperties; /** * 使用數(shù)據(jù)源配置信息,解密帳號(hào)和密碼后創(chuàng)建數(shù)據(jù)庫(kù)連接池 * @return */ @Bean public DataSource dataSource() { // TODO 對(duì)密碼解密并設(shè)置回去 basicProperties.setPassword(password); return new MyEncryptedDatasourceWrapper(); } }
這樣就完成了 Spring druid 數(shù)據(jù)源配置的解密處理了。
基礎(chǔ)鞏固
boolean proxyBeanMethods() 默認(rèn)值是 true. 從這個(gè)成員變量的注釋中,我們可以看到一句話 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.
其實(shí)從這句話,我們就可以初步得到我們想要的答案了:在帶有 @Configuration 注解的類中,一個(gè)帶有 @Bean 注解的方法顯式調(diào)用另一個(gè)帶有 @Bean 注解的方法,返回的是共享的單例對(duì)象。
參考文檔:《Component 和 Configuration 區(qū)別》
額外嘗試
加密數(shù)據(jù)源配置的解密流程,核心在 DataSourceProperties
這個(gè)實(shí)例裝配完成后修改密碼信息,嘗試自定義一個(gè) @Configuration
中 @Bean
注入一個(gè) DataSourceProperties
實(shí)例,但是這個(gè)對(duì)象到了 Druid 自動(dòng)注入類那里,屬性還是沒有發(fā)生變化:
@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
那使用的也是這個(gè)實(shí)例,單步調(diào)試對(duì)象地址是一樣的,但是改的屬性沒有生效。
數(shù)據(jù)源實(shí)例化配置時(shí)引用的屬性:
但是兩個(gè)地方的屬性卻不同,前一步解密的信息并沒有傳遞到真正使用的地方。 理論上,同一個(gè)對(duì)象,前面修改了屬性,這里同一個(gè)線程里面,屬性應(yīng)該變化了才對(duì)呢!不得其解。
啟示錄
回顧開頭的三個(gè)問題:
- 自定的
Configuration
類中的@Bean
注入一個(gè)DruidDataSource
,為什么比自動(dòng)裝配的時(shí)機(jī)早呢?因?yàn)?@Bean
屬于當(dāng)前項(xiàng)目掃描路徑,它里面的類注入優(yōu)先級(jí)高于第三方 jar 包中的spring.factories
的裝配類。 - 如果自定義一個(gè)自動(dòng)裝配類, 包含
DataSourceProperties
屬性,對(duì)它的帳號(hào)密碼解密后,讓它在DruidDataSourceAutoConfigure
類之前裝配,怎么實(shí)現(xiàn)呢?嘗試定義一個(gè)裝配類 @Bean 裝配一個(gè)數(shù)據(jù)源DataSourceProperties
對(duì)象,并修改配置。 - 自動(dòng)裝配類的工作原理是什么?注入優(yōu)先級(jí)怎么確定的?
spring.factories
的本質(zhì)是 SPI,它針對(duì)的是第三方 jar 包,不需要手動(dòng)配置掃描路徑,又需要自動(dòng)注入的情況,是各種 starter 定義底層實(shí)現(xiàn)途徑。
優(yōu)先級(jí):本地掃描路徑的 Configuration -> 實(shí)現(xiàn)了 BeanDefinitionRegistryPostProcessor 接口的類 -> spring.factories 中的自動(dòng)裝配類。
以上就是攔截Druid數(shù)據(jù)源自動(dòng)注入帳密解密實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Druid注入帳密解密攔截的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解
下面小編就為大家分享一篇jxl 導(dǎo)出數(shù)據(jù)到excel的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2017-12-12springboot+idea+maven 多模塊項(xiàng)目搭建的詳細(xì)過程(連接數(shù)據(jù)庫(kù)進(jìn)行測(cè)試)
這篇文章主要介紹了springboot+idea+maven 多模塊項(xiàng)目搭建的詳細(xì)過程(連接數(shù)據(jù)庫(kù)進(jìn)行測(cè)試),本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08解決springmvc項(xiàng)目中使用過濾器來解決請(qǐng)求方式為post時(shí)出現(xiàn)亂碼的問題
這篇文章主要介紹了springmvc項(xiàng)目中使用過濾器來解決請(qǐng)求方式為post時(shí)出現(xiàn)亂碼的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Spring-Boot 集成Solr客戶端的詳細(xì)步驟
本篇文章主要介紹了Spring-Boot 集成Solr客戶端的詳細(xì)步驟,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11