Spring + MyBatis 資料庫連線加密實現方式
近期因專案需要,客戶要求資料庫使用者名稱和密碼需加密實現連線。專案實現框架SSM,上網查閱一番資料後,發現有部分資料分享的不是很完善,在此寫下隨筆,以便大家採納及提出建議。有寫的不對的地方,歡迎給予指正。
以下來介紹我的實現方式
1、配置 jdbc.properties 配置檔案,該檔案可放置在src同級目錄下,其中的SIT環境和PRD環境的引數我就給刪掉了,可以根據自身專案實際情況,決定要配置幾個引數。這裡需要注意一點,配置檔案中的使用者名稱的KEY一定不能使用username,否則會帶來不必要的麻煩,無法正確連線資料庫,具體原因還請知道的同學回覆分享下。
# 資料庫連線密文配置 # AES加密方式 #driver jdbc.driver = oracle.jdbc.driver.OracleDriver #url jdbc.url = jdbc:oracle:thin:@127.0.0.1:1521/orcl #username jdbc.user = test #password fms jdbc.password = test #SITurl 測試環境 jdbc.SITurl = #SITusername jdbc.SITuser = #SITpassword jdbc.SITpassword = #PRDurl 生產環境 jdbc.PRDurl = #PRDusername jdbc.PRDuser = #PRDpassword jdbc.PRDpassword = ##加密狀態是否已加密 0-否 1-是 jdbc.isencoder = 0
2、修改 Spring 配置檔案 applicationContext-config.xml中 資料來源配置引數,如下,部分資料來源配置就沒有貼出來啦
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${jdbc.driver}" /> <property name="url" value="${jdbc.url}" /> <property name="username" value="${jdbc.user}" /> <property name="password" value="${jdbc.password}" /> </bean>
並需要新增如下配置來讀取配置檔案,location="屬性檔案,多個之間逗號分隔",ignore-unresolvable是否忽略解析不到的屬性,如果不忽略,找不到將丟擲異常。
<context:property-placeholder location="classpath:jdbc.properties" ignore-unresolvable="true"/>
3、建立自己的加密工具類,和引數重寫工具類,加密工具類就百度一下有很對,加密方式可以自行百度了,這裡我使用的是AES加密方式,下面我來貼一下我的配置檔案引數重寫的工具類
/** * 配置檔案工具類 * 實現配置檔案重寫 * * @author Fyq * */ public class PropUtil { private static Logger logger = LoggerFactory.getLogger(PropUtil.class); private static Properties properties = new Properties(); private static String filename; public static String getProperty(String key) { return properties.getProperty(key); } /** * 配置檔案引數重寫 * * @param key * @param value */ public static void setProperty(String key, String value) { try { FileInputStream inputStream = new FileInputStream( new File(Thread.currentThread().getContextClassLoader().getResource(filename).getPath())); Properties prop = new Properties(); prop.load(inputStream); prop.setProperty(key, value); FileOutputStream outputStream = new FileOutputStream( new File(Thread.currentThread().getContextClassLoader().getResource(filename).getPath())); prop.store(outputStream, "Update '" + key + "' value"); outputStream.flush(); outputStream.close(); properties.setProperty(key, value); } catch (FileNotFoundException e) { logger.error("load properties file error:" + e); e.printStackTrace(); } catch (IOException e) { logger.error("load properties file error:" + e); e.printStackTrace(); } } /** * 啟動時載入properties配置檔案 * * @param filePath */ public static void loadFile(String filePath) { try { properties.clear(); filename = filePath; properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(filePath)); } catch (IOException e) { logger.error("載入配置檔案錯誤:" + e); e.printStackTrace(); } } }
4、接著需要重寫 Spring容器的監聽器 ContextLoaderListener
/**
* 重寫Spring容器的監聽器
* 實現配置檔案屬性加密
* @author Fyq
*
*/
public class JdbcSafetyListener extends ContextLoaderListener {
protected final Logger logger = LoggerFactory.getLogger(JdbcSafetyListener.class);
// 屬性需與配置檔案的KEY保持一致
private String[] encryptPropNames = { "jdbc.user", "jdbc.password", "jdbc.SITuser", "jdbc.SITpassword",
"jdbc.PRDuser", "jdbc.PRDpassword" };
/**
* 初始化時載入properties配置檔案
*
* 重寫properties配置檔案,為key值加密
*/
@Override
public void contextInitialized(ServletContextEvent event) {
try {
PropUtil.loadFile("jdbc.properties");
// 配置檔案為未加密狀態
if (PropUtil.getProperty("jdbc.isencoder").equals("0")) {
for (String string : encryptPropNames) {
// 獲取需加密字串
String propertyValue = PropUtil.getProperty(string);
// 加密密文
String encryptValue = AESUtil.getEncryptString(propertyValue);
log.info(string + "---->" + encryptValue);
PropUtil.setProperty(string, encryptValue);
}
// 設定配置檔案加密狀態
PropUtil.setProperty("jdbc.isencoder", "1");
}
} catch (Exception e) {
log.error("引數載入失敗");
log.debug(e.getMessage(), e);
}
}
/**
* 最後處理的事情
*/
@Override
protected void finalize() throws Throwable {
// 關閉自動執行批處理
super.finalize();
log.info("引數載入成功");
}
}
監聽器實現之後,在web.xml配置這個監聽器,啟動容器時,就會預設執行它實現的方法。
<listener>
<listener-class>com.it.csdn.jdbcDecode.JdbcSafetyListener</listener-class>
</listener>
至此,我們的專案工程啟動後,工作域路徑\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\工程名\WEB-INF\classes\jdbc.properties中的屬性就會被重寫成密文
Linux環境下也在相關工程檔案加下可以找到,這裡就不貼出來給大家看了。
但是,我們還不能實現資料庫連線,因為資料庫user和密碼都是密文,那麼我們還需要實現使用者名稱和密碼的解密
5、實現資料庫使用者名稱和密碼的解密方式,這個時候就用到了 Spring中提供著一個 PropertyPlaceholderConfigurer ,其作用就是我們系統初始化的時候,系統自動讀取jdbc.properties配置檔案中的key value(鍵值對),然後對我們系統進行定製的初始化。
/**
* 資料庫配置檔案解密
*
* @author Fyq
*
*/
public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
protected final Logger logger = LoggerFactory.getLogger(EncryptPropertyPlaceholderConfigurer.class);
// 屬性需與配置檔案的KEY保持一致
private String[] encryptPropNames = { "jdbc.user", "jdbc.password", "jdbc.SITuser", "jdbc.SITpassword",
"jdbc.PRDuser", "jdbc.PRDpassword" };
@Override
protected String convertProperty(String propertyName, String propertyValue) {
// 如果在加密屬性名單中發現該屬性
if (isEncryptProp(propertyName)) {
logger.info(propertyName + "======>>" + propertyValue);
String decryptValue = AESUtil.getDecryptString(propertyValue);
System.out.println(decryptValue);
return decryptValue;
} else {
return propertyValue;
}
}
private boolean isEncryptProp(String propertyName) {
for (String encryptName : encryptPropNames) {
if (encryptName.equals(propertyName)) {
return true;
}
}
return false;
}
}
PropertyPlaceholderConfigurer重寫後,需要在applicationContext-config.xml下新增如下配置來載入該配置檔案解密方式
<bean class="com.it.csdn.jdbcDecode.EncryptPropertyPlaceholderConfigurer" p:locations="classpath:jdbc.properties"></bean>
至此資料庫明文配置,密文載入就已經實現。
另外說一下,還有一種實現方式,是在配置檔案中直接配置密文的方式,可以不必使用上述步驟中的Spring容器的監聽器去重寫配置檔案。個人感覺不是很方便,說一下其中缺點吧,,直接配置密文,不利於後面其他人員的維護,資料庫使用者名稱密碼如果忘記就很尷尬,還有一點就是,windows 和 linux環境下 加密演算法不一致,導致的密文也不一樣,換環境的時候,需要來回修改配置檔案中的密文引數非常的麻煩。
希望此篇文章可以幫助到那些急需資料庫加密連線實現的人
備註:文章中部分技術實現是參考網路中一些資料。