最近的項目有這樣一個需求:spring配置文件中的數據源配置需要加密,也就是讀取的jdbc.properties文件中的url、username、password等參數需要進行DES加密,然後在spring把參數賦值給數據源之前解密。
先來看看Spring的一段配置:
<context:property-placeholder file-encoding="UTF-8" location="classpath:/config.properties,classpath:/jdbc.properties" ignore-resource-not-found="true" />
這裏的配置就是spring在項目啓動時將配置文件中的屬性讀取到了spring容器中。此處這段配置其實就是創建PropertySourcesPlaceholderConfigurer這個類來進行的管理。各位按ctrl點擊標籤可以看到spring-context.xsd中的詳細信息。
PropertySourcesPlaceholderConfigurer類經過一系列繼承關係,實質是一個容器後管理器。debug走裏面的一段代碼如下:
進入這個mergeProperties()方法裏面看:
可以看到localOverride這個屬性是用來控制是否覆蓋Spring讀取的屬性配置。並且下面緊跟着判斷this.localProperties!=null,如果localProperties不爲null的話,會讀取這些配置信息到spring容易中並且覆蓋spring中已存在的屬性。
知道這些後,我們在來看看如下配置:
<!-- 這裏的local-override="true" 就是覆蓋spring容器中已存在的屬性,properties-ref="dataSourceProperties" 是指定自己的properties -->
<context:property-placeholder local-override="true" properties-ref="dataSourceProperties" file-encoding="UTF-8" location="classpath:/config.properties,classpath:/jdbc.properties" ignore-resource-not-found="true" />
<!-- 這個類是我自定義的,用來解密jdbc.properties中的屬性之後然後存放到Properties類中 -->
<bean id="dataSourceProperties" class="com.sh.point.spring.propertiesHandle.DataSourceProperties">
<constructor-arg value="point.read.jdbc.username,point.read.jdbc.password,point.read.jdbc.url,point.write.jdbc.username,point.write.jdbc.password,point.write.jdbc.url"/>
</bean>
下面是我的DataSourceProperties類:
package com.sh.point.spring.propertiesHandle;
import com.sh.point.constant.Constant;
import com.sh.point.utils.DES;
import java.io.IOException;
import java.util.Properties;
/**
* 數據源配置參數處理
* <p/>
* 配置信息事先被DES加密處理,需要在此解密然後綁定到數據源
* Created by Alvin on 2016/7/31.
*/
public class DataSourceProperties extends Properties {
/**
* 構造方法
* @param propertyNames 需要解密的屬性名稱
*/
public DataSourceProperties(String[] propertyNames) {
try {
this.load(DataSourceProperties.class.getResourceAsStream("/jdbc.properties"));
for (String propertyName : propertyNames) {
decrypt(propertyName);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 解密
*/
private void decrypt(String propertyName) {
String value = DES.decrypt(this.getProperty(propertyName), Constant.ENCRYPT_KEY);
this.setProperty(propertyName, value);
}
}
然後我的數據源配置如下:
<bean id="pointRead" class="com.atomikos.jdbc.AtomikosDataSourceBean"
init-method="init" destroy-method="close">
<description>mysql xa datasource</description>
<property name="uniqueResourceName">
<value>pointRead1</value>
</property>
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="user">${point.read.jdbc.username}</prop>
<prop key="password">${point.read.jdbc.password}</prop>
<prop key="URL">${point.read.jdbc.url}</prop>
</props>
</property>
<!-- 連接池裏面連接的個數? -->
<property name="poolSize" value="3"/>
</bean>
jdbc.properties裏面的屬性加密過後如下:
point.read.jdbc.url=csRbLXGXGZ65TOILxkSPCOyJksX4F0kyPy/iKeGKC0g8F4eKZFmW7ynoYjceDsrfhTtF7TC/dM0YgXAYcd9DjB5OdIulTJvHkqD458IMRC0m7pmKQb26lLj3VF0jlh+gZegrg8Y/D7o=
point.read.jdbc.username=BbeUz7wzHcE=
point.read.jdbc.password=XcZSWnefvog=
point.write.jdbc.url=csRbLXGXGZ65TOILxkSPCOyJksX4F0kyPy/iKeGKC0g8F4eKZFmW7ynoYjceDsrfhTtF7TC/dM0YgXAYcd9DjB5OdIulTJvHkqD458IMRC0m7pmKQb26lLj3VF0jlh+gZegrg8Y/D7o=
point.write.jdbc.username=BbeUz7wzHcE=
point.write.jdbc.password=XcZSWnefvog=
這樣就實現了上面的需求。properties文件中的屬性加密,然後spring讀取的時候先由上面的自己寫的DataSourceProperties類來進行解密並且存儲替換,然後spring就會把解密過後的屬性存放在容器,並且根據spring配置文件中的EL表達式注入到數據源中。
搞定!