继上一篇
扩展Spring-实现对外部引用的属性文件的 属性值 进行加密、解密 ,这次要实现的是对整个外部属性文件进行加密,Spring在加载这个外部属性文件时进行解密。
分析过程与在
扩展Spring-实现对外部引用的属性文件的 属性值 进行加密、解密 中介绍的基本一致,只不过这次的入口就在 PropertiesLoaderSupport.java 这个抽象类的loadProperties方法。代码片段:(注意注释部分)
@Override
/** *//**
* Load properties into the given instance.
* @param props the Properties instance to load into
* @throws java.io.IOException in case of I/O errors
* @see #setLocations
*/
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (int i = 0; i < this.locations.length; i++) {
Resource location = this.locations[i];
if (logger.isInfoEnabled()) {
logger.info("Loading properties file from " + location);
}
InputStream is = null;
try {
// 属性文件的输入流
// 因为这个属性文件是我们事先加密过的
// 所以在这里我们只要将此输入流解密 再将其作为输入流返回
// 其他的工作就按照Spring的流程即可
is = location.getInputStream();
if (location.getFilename().endsWith(XML_FILE_EXTENSION)) {
this.propertiesPersister.loadFromXml(props, is);
} else {
if (this.fileEncoding != null) {
this.propertiesPersister.load(props, new InputStreamReader(is,
this.fileEncoding));
} else {
this.propertiesPersister.load(props, is);
}
}
} catch (IOException ex) {
if (this.ignoreResourceNotFound) {
if (logger.isWarnEnabled()) {
logger.warn("Could not load properties from " + location + ": "
+ ex.getMessage());
}
} else {
throw ex;
}
} finally {
if (is != null) {
is.close();
}
}
}
}
}
开始我们的实现过程:
1.生成密钥:DesUtil.java 这其中包括生成密钥,对文件进行加密、解密操作。
此解密方法返回输入流对象,以便在 spring的loadProperties方法中使用。
最终密钥文件生成为:mytest.key
package com.sec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.security.Key;
import java.security.SecureRandom;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import org.apache.commons.io.IOUtils;
public class DesUtil {
/** *//**
* 生成密钥
*
* @param keyPath 密钥文件
*/
public static void createDesKey(String keyPath) {
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
SecureRandom sr = new SecureRandom();
KeyGenerator kg = KeyGenerator.getInstance("DES");
kg.init(sr);
fos = new FileOutputStream(keyPath);
oos = new ObjectOutputStream(fos);
// 生成密钥
Key key = kg.generateKey();
oos.writeObject(key);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(fos);
IOUtils.closeQuietly(oos);
}
}
/** *//**
* 获得密钥
*
* @param keyPath
* @return
*/
public static Key getKey(String keyPath) {
Key kp = null;
InputStream is = null;
ObjectInputStream ois = null;
try {
is = ClassLoader.getSystemClassLoader().getResourceAsStream(keyPath);
return getKey(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(ois);
}
return kp;
}
/** *//**
* 获得密钥
* @param is
* @return
*/
public static Key getKey(InputStream is) {
Key key = null;
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(is);
key = (Key)ois.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
IOUtils.closeQuietly(ois);
}
return key;
}
/** *//**
* 加密源文件并保存到目标文件
*
* @param srcFile
* 源文件
* @param destFile
* 目标文件
* @param key
* 加密用的Key
* @throws Exception
*/
public static void encrypt(String srcFile, String destFile, Key key) throws Exception {
InputStream is = null;
OutputStream out = null;
CipherInputStream cis = null;
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
is = ClassLoader.getSystemClassLoader().getResourceAsStream(srcFile);
out = new FileOutputStream(destFile);
cis = new CipherInputStream(is, cipher);
byte[] buffer = new byte[1024];
int r;
while ((r = cis.read(buffer)) > 0) {
out.write(buffer, 0, r);
}
} finally {
IOUtils.closeQuietly(cis);
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(out);
}
}
/** *//**
* 解密文件
*
* @param file
* @param key
* @return
* @throws Exception
*/
public static InputStream decrypt(InputStream is, Key key) throws Exception {
OutputStream out = null;
CipherOutputStream cos = null;
ByteArrayOutputStream bout = null;
try {
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.DECRYPT_MODE, key);
bout = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int count = 0;
while ((count = is.read(buf)) != -1) {
bout.write(buf, 0, count);
buf = new byte[1024];
}
byte[] orgData = bout.toByteArray();
byte[] raw = cipher.doFinal(orgData);
return new ByteArrayInputStream(raw);
} finally {
IOUtils.closeQuietly(cos);
IOUtils.closeQuietly(out);
IOUtils.closeQuietly(bout);
}
}
}
2.对jdbc.properties文件进行加密:加密的方法为DesUtil的encrypt方法。
jdbc.properties内容如下:
driver=oracle.jdbc.OracleDriver
dburl=jdbc:oracle:thin:@127.0.0.1:1521:root
username=blogjava
password=javadesps
加密后得到文件desJdbc.properties,其内容如下:
(ä8i6nOIÏ,d¢MÖÇäðëñÖn$BÞd|ê¾. ÓF—pêLylGýÓ$Iv'ÕJô
3.为了进行测试,新建SpringTestBean.java类,该类中只包含driver、url、username、password属性以及get、set方法。
package com.spring;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
public class SpringTestBean {
private String driver = null;
private String url = null;
private String username = null;
private String password = null;
// get set 方法略
/** *//**
* 重写toString方法 观察测试结果用
*/
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("driver", driver)
.append("url", url).append("username", username).append("password", password)
.toString();
}
}
4.配置applicationContext.xml文件。内容如下:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"
default-lazy-init="true">
<bean id="propertyConfigurer"
class="com.spring.DecryptPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:com/spring/desJdbc.properties</value><!-- 加密后文件 -->
</list>
</property>
<property name="fileEncoding" value="utf-8"/>
<property name="keyLocation" value="classpath:com/spring/mytest.key" /><!-- 密钥文件位置 -->
</bean>
<!-- 测试用bean -->
<bean id="testBean" class="com.spring.SpringTestBean" destroy-method="close">
<property name="driver" value="${driver}" />
<property name="url" value="${dburl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
</beans>
5.实现自己的PropertyPlaceholderConfigurer类----DecryptPropertyPlaceholderConfigurer.java,继承自Spring的PropertyPlaceholderConfigurer类:
package com.spring;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.Resource;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
import com.sec.DesUtil;
public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
private Resource[] locations;
private Resource keyLocation;
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
private String fileEncoding = "utf-8";
private boolean ignoreResourceNotFound = false;
@Override
public void setLocations(Resource[] locations) {
this.locations = locations;
}
@Override
public void setFileEncoding(String encoding) {
this.fileEncoding = encoding;
}
@Override
public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
this.ignoreResourceNotFound = ignoreResourceNotFound;
}
public void setKeyLocation(Resource keyLocation) {
this.keyLocation = keyLocation;
}
@Override
/** *//**
* Load properties into the given instance.
* @param props the Properties instance to load into
* @throws java.io.IOException in case of I/O errors
* @see #setLocations
*/
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (int i = 0; i < this.locations.length; i++) {
Resource location = this.locations[i];
InputStream is = null; // 属性文件输入流
InputStream keyStream = null; // 密钥输入流
InputStream readIs = null; // 解密后属性文件输入流
try {
// 属性文件输入流
is = location.getInputStream();
// 密钥输入流
keyStream = keyLocation.getInputStream();
// 得到解密后的输入流对象
readIs = DesUtil.decrypt(is, DesUtil.getKey(keyStream));
// 以下操作按照Spring的流程做即可
if (location.getFilename().endsWith(XML_FILE_EXTENSION)) {
this.propertiesPersister.loadFromXml(props, readIs);
} else {
if (this.fileEncoding != null) {
this.propertiesPersister.load(props, new InputStreamReader(readIs,
this.fileEncoding));
} else {
this.propertiesPersister.load(props, readIs);
}
}
} catch (Exception ex) {
if (this.ignoreResourceNotFound) {
if (logger.isWarnEnabled()) {
logger.warn("Could not load properties from " + location + ": "
+ ex.getMessage());
}
}
} finally {
IOUtils.closeQuietly(is);
IOUtils.closeQuietly(keyStream);
IOUtils.closeQuietly(readIs);
}
}
}
}
}
6.测试代码:SpringCryptTest.java,这个代码很简单,就是用Spring去取得我们的测试bean----SpringTestBean,打印它的属性,测试已经加密过的desJdbc.properties文件,能否被Spring正确解密并加载。
package com.spring;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringCryptTest {
public static void main(String args[]) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(
"com/spring/applicationContext.xml");
SpringTestBean bean = (SpringTestBean)ctx.getBean("testBean");
System.out.println(bean.toString());
}
}
执行结果:
SpringTestBean[driver=oracle.jdbc.OracleDriver,url=jdbc:oracle:thin:@127.0.0.1:1521:root,username=blogjava,password=javadesps]
OK。到此,Spring已经正确解密并加载了此外部属性文件。
本文为原创,欢迎转载,转载请注明出处
BlogJava。