繼上一篇
擴展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,其內容如下:
(ä8i6n??O?IÏ,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。