导读
概述
对于不敏感的属性信息,以明文形式出现在属性文件中是合适的,但是如果属性信息是数据库用户名和密码等敏感信息,一般希望以密文的方式保存。
这就要求对应用程序配置文件的某些属性进行加密,让Spring容器在读取属性文件后,在内存中对属性进行解密,然后将解密后的属性值赋给目标对象。
我们来看下 PropertyPlaceholderConfigurer的继承关系
PropertyResourceConfigurer类中有几个方法
实例
代码已托管到Github—> https://github.com/yangshangwei/SpringMaster
DES加密解密工具类
信息的加密分为对称和非对称两种方式, 前者表示加密后的信息可以解密为原值,而后者则不能根据加密后的信息还原为原值。
MD5属于非对称加密, DES属于对称加密。
先用DES对属性值进行加密,在读取到属性值时,在用DES进行解密。
DES加密解密工具类
package com.xgj.ioc.propertyplacehoderEncryption;
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 DESUtils {
private static Key key;
// 指定DES加密解密用的密钥
private static String KEY_STR = "myKey";
static {
try {
KeyGenerator generator = KeyGenerator.getInstance("DES");
generator.init(new SecureRandom(KEY_STR.getBytes()));
key = generator.generateKey();
generator = null;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
*
*
* @Title: getEncryptString
*
* @Description: 对字符串进行加密,返回BASE64编码的加密字符串
*
* @param str
* @return
*
* @return: String
*/
public static String getEncryptString(String str) {
BASE64Encoder base64en = new BASE64Encoder();
try {
byte[] strBytes = str.getBytes("UTF8");
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptStrBytes = cipher.doFinal(strBytes);
return base64en.encode(encryptStrBytes);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
*
*
* @Title: getDecryptString
*
* @Description:对BASE64编码的加密字符串进行解密,返回解密后的字符串
*
* @param str
* @return
*
* @return: String
*/
public static String getDecryptString(String str) {
BASE64Decoder base64De = new BASE64Decoder();
try {
byte[] strBytes = base64De.decodeBuffer(str);
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptStrBytes = cipher.doFinal(strBytes);
return new String(decryptStrBytes, "UTF8");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
*
*
* @Title: main
*
* @Description: 测试方法
*
* @param args
* @throws Exception
*
* @return: void
*/
public static void main(String[] args) throws Exception {
System.out.println(getEncryptString("cc"));
System.out.println(getEncryptString("zsmart2017"));
System.out.println(getDecryptString("SkR6wWI9iws="));
System.out.println(getDecryptString("lSR/mscM1NE3sM98QFjAdw=="));
}
}
使用密文版的属性文件
- 运行 DESUtils, 得到 用户名和密码的加密字符串
- 修改jdbc.properties
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@172.25.246.11:1521:xgj
jdbc.username=SkR6wWI9iws=
jdbc.password=lSR/mscM1NE3sM98QFjAdw==
PropertyPlaceholderConfigurer 本身不支持密文版的属性文件,不过我们可以扩展该类,重写 String convertProperty(String propertyName, String propertyValue)方法
package com.xgj.ioc.propertyplacehoderEncryption;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
/**
*
*
* @ClassName: EncryptPropertyPlaceholderConfigurer
*
* @Description: 继承PropertyPlaceholderConfigurer,重写convertProperty方法,对属性进行解密
*
* @author: Mr.Yang
*
* @date: 2017年8月6日 下午11:01:23
*/
public class EncryptPropertyPlaceholderConfigurer extends
PropertyPlaceholderConfigurer {
// 对应jdbc.properties中的key
private String[] encryptPropNames = { "jdbc.username", "jdbc.password" };
@Override
protected String convertProperty(String propertyName, String propertyValue) {
if (isEncryptProp(propertyName)) {
String decryptValue = DESUtils.getDecryptString(propertyValue);
System.out.println("解密后的字符串:" + decryptValue);
return decryptValue;
} else {
return propertyValue;
}
}
/**
* 判断是否是加密的属性
*
* @param propertyName
* @return
*/
private boolean isEncryptProp(String propertyName) {
for (String encryptPropName : encryptPropNames) {
if (encryptPropName.equals(propertyName)) {
return true;
}
}
return false;
}
}
EncryptPropertyPlaceholderConfigurer使用DESUtils中的方法解密加密后的字符串。
修改配置文件,引用自定义的EncryptPropertyPlaceholderConfigurer
<!-- 引入JDBC属性文件 加密-->
<bean class="com.xgj.ioc.propertyplacehoderEncryption.EncryptPropertyPlaceholderConfigurer"
p:location="classpath:spring/jdbc.properties"
p:fileEncoding="utf-8"/>
使用自定义的属性加载器后,就无法使用context:property-placeholder属性加载配置文件了,必须使用传统的方式引用加密版的属性文件,如上
完整的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
<context:component-scan base-package="com.xgj.ioc.propertyplacehoderEncryption"/>
<!-- 引入JDBC属性文件 加密-->
<bean class="com.xgj.ioc.propertyplacehoderEncryption.EncryptPropertyPlaceholderConfigurer"
p:location="classpath:spring/jdbc.properties"
p:fileEncoding="utf-8"/>
<!-- 通过属性名引用属性值 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"/>
<!-- 配置Jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />
</beans>
测试类
package com.xgj.ioc.propertyplacehoderEncryption;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class PropertyPlaceHoderEncryptionTest {
private final static String MATCH_COUNT_SQL = " SELECT count(*) FROM temp_user "
+ " WHERE user_name =? and password=? ";
private JdbcTemplate jdbcTemplate;
@Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
/**
*
*
* @Title: getMatchCount
*
* @Description: 根据用户名和密码判断用户是否存在
*
* @param username
* @param password
*
* @return: int
*/
public int getMatchCount(String username, String password) {
return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[] {
username, password }, Integer.class);
}
/**
*
*
* @Title: main
*
* @Description: 测试
*
* @param args
*
* @return: void
*/
public static void main(String[] args) {
// 加载Spring配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:com/xgj/ioc/propertyplacehoderEncryption/beans.xml");
// 获取通过注解标注的Bean
PropertyPlaceHoderEncryptionTest propertyplacehoderEncryption = ctx
.getBean("propertyPlaceHoderEncryptionTest",
PropertyPlaceHoderEncryptionTest.class);
// 调用方法
int count = propertyplacehoderEncryption.getMatchCount("xgj", "123456");
System.out.println("匹配的用户数量:" + count);
}
}
测试结果