ALL is Well!

敏捷是一条很长的路,摸索着前进着

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  30 随笔 :: 23 文章 :: 71 评论 :: 0 Trackbacks
继上一篇 扩展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
posted on 2010-10-02 12:44 李 明 阅读(4360) 评论(8)  编辑  收藏 所属分类: Spring

评论

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-06-13 17:05 gsf
我测试不成功啊
  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-06-13 17:05 gsf
我的QQ:407584979  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-13 11:28 admin_yin
@gsf
我测试也没有成功,请问你的可以了吗?QQ:26509925  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-18 21:32 李 明
把不成功的信息发一下,比如出现什么样的情况,描述一下,有异常,贴一下。@gsf
  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-18 21:32 李 明
把不成功的信息发一下,比如出现什么样的情况,描述一下,有异常,贴一下。@admin_yin
  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-19 18:07 admin_yin
@李 明
我测试时成功了,但是要是连接数据库话,不知在application中怎么配置了,目前配置如:
<bean id="propertyConfigurer"
class="config.ReadEncryptedPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:config/db.properties</value><!-- 加密后文件 -->
</list>
</property>
<property name="fileEncoding" value="utf-8"/>
<property name="keyLocation" value="classpath:config/key.dat" /><!-- 密钥文件位置 -->
</bean>

<!-- 测试用bean -->
<bean id="testBean" class="config.SpringTestBean" >
<property name="driver" value="${driver}"/>
<property name="url" value="${dburl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>


<bean id="datasource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName"
value="com.microsoft.sqlserver.jdbc.SQLServerDriver">
</property>
<!-- <property name="url"-->
<!-- value="jdbc:sqlserver://localhost:1433;DatabaseName=stu">-->
<!-- </property>-->
<!-- <property name="username" value="sa"></property>-->
<!-- <property name="password" value="sa"></property>-->
</bean>
<bean id="sessionfactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="datasource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>vo/Student.hbm.xml</value></list>
</property>
</bean>
<bean id="StudentDAO" class="vo.StudentDAO">
<property name="sessionFactory">
<ref bean="sessionfactory" />
</property>
</bean>  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-20 11:52 admin_yin
@李 明
我在Spring里面配置,当启动Tomcat的时候能解密,但是出现了如以下的错误:
信息: Initializing Spring root WebApplicationContext
dburl=jdbc:sqlserver://localhost:1433;databaseName=stu

password=sa

driver=com.microsoft.sqlserver.jdbc.SQLServerDriver

username=sa

2011-9-20 11:49:33 org.apache.catalina.core.StandardContext listenerStart
严重: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener
org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name 'datasource' defined in class path resource [applicationContext.xml]: Could not resolve placeholder 'driver'
at org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.processProperties(PropertyPlaceholderConfigurer.java:268)
at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:75)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:553)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:527)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:362)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:45)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4342)
at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:791)
at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:771)
at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:525)
at org.apache.catalina.startup.HostConfig.deployDirectory(HostConfig.java:926)
at org.apache.catalina.startup.HostConfig.deployDirectories(HostConfig.java:889)
at org.apache.catalina.startup.HostConfig.deployApps(HostConfig.java:492)
at org.apache.catalina.startup.HostConfig.start(HostConfig.java:1149)
at org.apache.catalina.startup.HostConfig.lifecycleEvent(HostConfig.java:311)
at org.apache.catalina.util.LifecycleSupport.fireLifecycleEvent(LifecycleSupport.java:117)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1053)
at org.apache.catalina.core.StandardHost.start(StandardHost.java:719)
at org.apache.catalina.core.ContainerBase.start(ContainerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(StandardEngine.java:443)
at org.apache.catalina.core.StandardService.start(StandardService.java:516)
at org.apache.catalina.core.StandardServer.start(StandardServer.java:710)
at org.apache.catalina.startup.Catalina.start(Catalina.java:578)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:288)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:413)
2011-9-20 11:49:33 org.apache.catalina.core.StandardContext start

----------------------------------------------------
难道配置不对吗?配置
<bean id="propertyConfigurer"
class="test0x31.ReadEncryptedPropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db.properties</value><!-- 加密后文件 -->
</list>
</property>
<property name="fileEncoding" value="utf-8"/>
<!-- <property name="keyLocation" value="classpath:config/key.dat" /> 密钥文件位置 -->
</bean>
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${dburl}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
<bean id="sessionfactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="datasource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.SQLServerDialect
</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>vo/Student.hbm.xml</value></list>
</property>
</bean>
<bean id="StudentDAO" class="vo.StudentDAO">
<property name="sessionFactory">
<ref bean="sessionfactory" />
</property>
</bean>  回复  更多评论
  

# re: 扩展Spring-实现对外部引用的属性文件进行加密、解密 2011-09-25 09:43 李 明
根据你的异常信息是说 没有 placeholder 关键字 driver,你检查一下config/db.properties 的配置,是否配置了 driver,或者关键字有没有写错。@admin_yin
  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: