在Spring项目中经常需要在jdbc.properties文件中写入数据库连接的用户名和密码,这样在连接传输中是不安全的,我们可以利用对称加密的方法对敏感数据进行加密,在使用的时候再进行解密。比如数据加密算法(Data Encryption Algorithm,DEA),是一种比较传统的加密方式,其加密运算、解密运算使用的是同样的密钥,信息的发送者和信息的接收者在进行信息的传输与处理时,必须共同持有该密码(称为对称密码)
我们创建一个DESUtil
类用于实现加密解密等相关操作。
首先定义一个静态方法用于实现密钥对象key
的生成。我们首先自定义一个随机数种子KEY_STR
用于生成随机数,接着创建随机数生成器secureRandom
并设置种子(相同的种子生成的随机数是一样的)。最后创建密钥生成器并用随机数对象初始化,用密钥生成器创建密钥对象。
package com.tory.shop.util;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESUtil {
private static final Key key;
private static final String KEY_STR = "ShopKey"; //密钥随机数种子
private static final String CHARSET = "UTF-8"; //编码方式
private static final String ALGORITHM = "DES"; //加密算法
static {
try {
//创建基于SHA1的安全随机数生成器
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
//设置随机数种子
secureRandom.setSeed(KEY_STR.getBytes());
//创建密钥生成器
KeyGenerator generator = KeyGenerator.getInstance(ALGORITHM);
//使用随机数初始化密钥生成器
generator.init(secureRandom);
//生成密钥对象
key = generator.generateKey();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
加密过程就是首先根据密钥创建加密对象cipher
,然后利用base64encoder
将字符串str先转为字节序列,用cipher进行加密后再重新编码为字符串
//加密字符串
public static String getEncryptString(String str) {
BASE64Encoder base64encoder = new BASE64Encoder();
try {
//创建加密对象
Cipher cipher = Cipher.getInstance(ALGORITHM);
//根据密钥初始化加密对象
cipher.init(Cipher.ENCRYPT_MODE, key);
//将字符串转化为字节序列
byte[] bytes = str.getBytes(CHARSET);
//对字节序列进行加密
byte[] encrypt_bytes = cipher.doFinal(bytes);
//将字节序列编码为字符串并返回
return base64encoder.encode(encrypt_bytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
解密过程和加密类似,先将字符串编码为字节序列,通过cipher解密后再编码为字符串返回
public static String getDecryptString(String str) {
BASE64Decoder base64decoder = new BASE64Decoder();
try {
byte[] bytes = base64decoder.decodeBuffer(str);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decrypt_bytes = cipher.doFinal(bytes);
return new String(decrypt_bytes, CHARSET);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
运行加密方法得到加密后的用户名和密码,填写到jdbc.properties文件中
public static void main(String[] args) {
System.out.println(getEncryptString("root"));
System.out.println(getEncryptString("root1234"));
}
原来的配置文件spring-dao.xml如下,通过property-placeholder
读取jbdc.properties文件并将读取到的数据库设置以${}
方式注入到c3p0连接池bean
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
......
现在使用自定义的beanEncryptPropertyPlaceholderConfigure
读取数据库变量并对其中加密的属性进行解密
<bean class="com.tory.shop.util.EncryptPropertyPlaceholderConfigure">
<property name="location" value="classpath:jdbc.properties"/>
bean>
EncryptPropertyPlaceholderConfigure实现如下,它继承自PropertyPlaceholderConfigurer
类并重写其convertProperty()
方法。对传入的propertyName
,判断其是否在需要解密的数组encrypt_properties
中,若在则将propertyValue
解密后返回,否则直接返回。
package com.tory.shop.util;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import java.util.Arrays;
import java.util.List;
public class EncryptPropertyPlaceholderConfigure extends PropertyPlaceholderConfigurer {
//要解密的属性
private List<String> encrypt_properties = Arrays.asList("jdbc.username", "jdbc.password");
@Override
protected String convertProperty(String propertyName, String propertyValue) {
if (encrypt_properties.contains(propertyName)) {
return DESUtil.getDecryptString(propertyValue);
} else {
return propertyValue;
}
}
}