引言
咱們公司從事的是信息安全涉密應用的一些項目研發一共有分為三步,相比較於一般公司和一般的項目,對於信息安全要求更加嚴格,領導要求數據量和用戶的用戶名及密碼信息都必需是要密文配置和存儲的,這就涉及到jdbc.properties文件中的數據庫的用戶名和密碼也是一樣的,需要配置問密文,在連接的時候再加載解密為明文進行數據庫的連接操作,以下就是實現過程,一共有分為三步。
一、創建DESUtil類
提供自定義密鑰,加密解密的方法。
1 package com.hzdy.DCAD.common.util; 2 3 import sun.misc.BASE64Decoder; 4 import sun.misc.BASE64Encoder; 5 import javax.crypto.Cipher; 6 import javax.crypto.KeyGenerator; 7 import java.security.Key; 8 import java.security.SecureRandom; 9 10 /** 11 * Created by Wongy on 2019/8/8. 12 */ 13 public class DESUtil { 14 private static Key key; 15 //自己的密鑰 16 private static String KEY_STR = "mykey"; 17 18 static { 19 try { 20 KeyGenerator generator = KeyGenerator.getInstance("DES"); 21 SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); 22 secureRandom.setSeed(KEY_STR.getBytes()); 23 generator.init(secureRandom); 24 key = generator.generateKey(); 25 generator = null; 26 } catch (Exception e) { 27 throw new RuntimeException(e); 28 } 29 } 30 31 /** 32 * 對字符串進行加密,返回BASE64的加密字符串 33 * 34 * @param str 35 * @return 36 * @see [類、類#方法、類#成員] 37 */ 38 public static String getEncryptString(String str) { 39 BASE64Encoder base64Encoder = new BASE64Encoder(); 40 try { 41 byte[] strBytes = str.getBytes("UTF-8"); 42 Cipher cipher = Cipher.getInstance("DES"); 43 cipher.init(Cipher.ENCRYPT_MODE, key); 44 byte[] encryptStrBytes = cipher.doFinal(strBytes); 45 return base64Encoder.encode(encryptStrBytes); 46 } catch (Exception e) { 47 throw new RuntimeException(e); 48 } 49 50 } 51 52 /** 53 * 對BASE64加密字符串進行解密 54 * 55 */ 56 public static String getDecryptString(String str) { 57 BASE64Decoder base64Decoder = new BASE64Decoder(); 58 try { 59 byte[] strBytes = base64Decoder.decodeBuffer(str); 60 Cipher cipher = Cipher.getInstance("DES"); 61 cipher.init(Cipher.DECRYPT_MODE, key); 62 byte[] encryptStrBytes = cipher.doFinal(strBytes); 63 return new String(encryptStrBytes, "UTF-8"); 64 } catch (Exception e) { 65 throw new RuntimeException(e); 66 } 67 68 } 69 70 71 public static void main(String[] args) { 72 String name = "dbuser"; 73 String password = "waction2016"; 74 String encryname = getEncryptString(name); 75 String encrypassword = getEncryptString(password); 76 System.out.println("encryname : " + encryname); 77 System.out.println("encrypassword : " + encrypassword); 78 79 System.out.println("name : " + getDecryptString(encryname)); 80 System.out.println("password : " + getDecryptString(encrypassword)); 81 } 82 }
二、 創建EncryptPropertyPlaceholderConfigurer類
建立與配置文件的關聯。
1 package com.hzdy.DCAD.common.util; 2 3 import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; 4 5 public class EncryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { 6 //屬性需與配置文件的KEY保持一直 7 private String[] encryptPropNames = {"jdbc.username", "jdbc.password"}; 8 9 @Override 10 protected String convertProperty(String propertyName, String propertyValue) { 11 12 //如果在加密屬性名單中發現該屬性 13 if (isEncryptProp(propertyName)) { 14 String decryptValue = DESUtil.getDecryptString(propertyValue); 15 System.out.println(decryptValue); 16 return decryptValue; 17 } else { 18 return propertyValue; 19 } 20 21 } 22 23 private boolean isEncryptProp(String propertyName) { 24 for (String encryptName : encryptPropNames) { 25 if (encryptName.equals(propertyName)) { 26 return true; 27 } 28 } 29 return false; 30 } 31 }
三、 修改配置文件 jdbc.properties
1 #加密配置之前 2 #jdbc.driver=com.mysql.jdbc.Driver 3 #jdbc.user=root 4 #jdbc.password=root 5 #jdbc.url=jdbc:mysql://localhost:3306/bookstore 6 7 #加密配置之后 8 jdbc.driver=com.mysql.jdbc.Driver 9 jdbc.user=Ov4j7fKiCzY= 10 jdbc.password=Ov4j7fKiCzY= 11 jdbc.url=jdbc:mysql://localhost:3306/bookstore
四、 修改spring-content.xml配置文件
1 將spring-context中的 2 <context:property-placeholder location="classpath:.properties" /> 3 修改為 4 <bean class="com.hzdy.DCAD.common.util.EncryptPropertyPlaceholderConfigurer"p:locations="classpath:*.properties"/> 5 //注意只能存在一個讀取配置文件的bean,否則系統只會讀取最前面的
注意:如果發現配置密文的username和password可以加載並解密成功,但是最后連接的時候還是以密文連接並報錯,這可能涉及到內存預加載的問題,項目一啟動,程序會加密密文的用戶名和密碼,就算最后解密成功了,最后連接數據庫讀取的卻還是密文,這時候我們可以自己重寫連接池的方法,讓spring-content.xml加載重寫的連接池方法,並在連接的時候再提前進行解密。
1 package com.thinkgem.jeesite.common.encrypt; 2 3 import java.sql.Connection; 4 import java.sql.SQLException; 5 import java.util.Properties; 6 7 import javax.security.auth.callback.PasswordCallback; 8 import com.alibaba.druid.util.DruidPasswordCallback; 9 10 /** 11 */ 12 @SuppressWarnings("serial") 13 public class DruidDataSource extends com.alibaba.druid.pool.DruidDataSource { 14 15 public PhysicalConnectionInfo createPhysicalConnection() throws SQLException { 16 String url = this.getUrl(); 17 Properties connectProperties = getConnectProperties(); 18 19 String user; 20 if (getUserCallback() != null) { 21 user = getUserCallback().getName(); 22 } else { 23 user = getUsername(); 24 } 25 //DES解密 26 user = DESUtils.getDecryptString(user); 27 String password = DESUtils.getDecryptString(getPassword()); 28 29 PasswordCallback passwordCallback = getPasswordCallback(); 30 31 if (passwordCallback != null) { 32 if (passwordCallback instanceof DruidPasswordCallback) { 33 DruidPasswordCallback druidPasswordCallback = (DruidPasswordCallback) passwordCallback; 34 35 druidPasswordCallback.setUrl(url); 36 druidPasswordCallback.setProperties(connectProperties); 37 } 38 39 char[] chars = passwordCallback.getPassword(); 40 if (chars != null) { 41 password = new String(chars); 42 } 43 } 44 45 Properties physicalConnectProperties = new Properties(); 46 if (connectProperties != null) { 47 physicalConnectProperties.putAll(connectProperties); 48 } 49 50 if (user != null && user.length() != 0) { 51 physicalConnectProperties.put("user", user); 52 } 53 54 if (password != null && password.length() != 0) { 55 physicalConnectProperties.put("password", password); 56 } 57 58 Connection conn; 59 60 long connectStartNanos = System.nanoTime(); 61 long connectedNanos, initedNanos, validatedNanos; 62 try { 63 conn = createPhysicalConnection(url, physicalConnectProperties); 64 connectedNanos = System.nanoTime(); 65 66 if (conn == null) { 67 throw new SQLException("connect error, url " + url + ", driverClass " + this.driverClass); 68 } 69 70 initPhysicalConnection(conn); 71 initedNanos = System.nanoTime(); 72 73 validateConnection(conn); 74 validatedNanos = System.nanoTime(); 75 76 setCreateError(null); 77 } catch (SQLException ex) { 78 setCreateError(ex); 79 throw ex; 80 } catch (RuntimeException ex) { 81 setCreateError(ex); 82 throw ex; 83 } catch (Error ex) { 84 createErrorCount.incrementAndGet(); 85 throw ex; 86 } finally { 87 long nano = System.nanoTime() - connectStartNanos; 88 createTimespan += nano; 89 } 90 91 return new PhysicalConnectionInfo(conn, connectStartNanos, connectedNanos, initedNanos, validatedNanos); 92 } 93 }
修改spring-content.xml文件的數據庫連接數配置
1 #修改之前 2 <!-- <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> --> 3 4 #修改之后 5 <bean id="dataSource"class="com.thinkgem.jeesite.common.encrypt.DruidDataSource" 6 init-method="init" destroy-method="close"> 7 <!-- 數據源驅動類可不寫,Druid默認會自動根據URL識別DriverClass --> 8 <property name="driverClassName" value="${jdbc.driver}" /> 10 <!-- 基本屬性 url、user、password --> 11 <property name="url" value="${jdbc.url}" /> 12 <property name="username" value="${jdbc.username}" /> 13 <property name="password" value="${jdbc.password}" /> 14 15 </bean>
至此,數據庫密文配置連接就完成了!