引言
咱们公司从事的是信息安全涉密应用的一些项目研发一共有分为三步,相比较于一般公司和一般的项目,对于信息安全要求更加严格,领导要求数据量和用户的用户名及密码信息都必需是要密文配置和存储的,这就涉及到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>
至此,数据库密文配置连接就完成了!