Spring对外部属性文件指定的某个属性进行加密、解密

摘要:
为了实现安全,我们通常加密密码。您可能希望看到db.properties是这样的:#数据库配置db。驱动程序=组织。postgresql。驱动程序数据库。url=jdbc:postgresql://10.166.176.127:5432/ivsdb.username=ivsadmindb.password={SMC}sYNzVKgIhOprkdGhCyt81w==db。name=ivs在这里,您可以看到密码属性值被加密,其他属性值保持不变,以实现安全目的。

[From] http://blog.csdn.net/ethanq/article/details/7333897


在我们开发当中,经常会用到spring框架来读取属性文件的属性值,然后使用占位符引用属性文件的属性值来简化配置以及使配置具有更高的灵活性和通用性。

   如下面的属性配置文件:db.properties 

   #数据库配置
   db.driver=org.postgresql.Driver
   db.url=jdbc:postgresql://10.166.176.127:5432/test
   db.username=ivsadmin
   db.password=123456
   db.name=ivs


   applicationContext.xml文件

[html] view plain copy
  1. <context:property-placeholder location="classpath:db.properties" />  
  2.    
  3.    <bean id="dataSource"  
  4. class="com.mchange.v2.c3p0.ComboPooledDataSource"  
  5. destroy-method="close">  
  6. <property name="driverClass" value="${db.driver}" />  
  7. <property name="jdbcUrl" value="${db.url}" />  
  8. <property name="user" value="${db.username}" />  
  9. <property name="password" value="${db.password}" />  
  10. <property name="checkoutTimeout" value="3000" />  
  11.    </bean>  

对于一些敏感的属性值,例如:密码属性。为了达到安全目的,我们一般会将密码进行加密。
可能希望用户看到db.properties是这样的:
#数据库配置
db.driver=org.postgresql.Driver
db.url=jdbc:postgresql://10.166.176.127:5432/ivs
db.username=ivsadmin
db.password={SMC}sYNzVKgIhOprkdGhCyt81w==
db.name=ivs

这里可以看到密码属性值是加密过的,其它的属性值不变,这样就达到安全目的。这里采用的是java的3DES加密,在前面的文章中 3DES加密、解密工具类 已经有介绍了

下面开始分析下我们的需求:
  在Spring中担负对外在化应用参数的配置的是PropertyPlaceholderConfigurer和PropertyOverrideConfigurer对象,PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessor接口,它能够对<bean/>中的属性值进行外在化管理。
  就像这样:

[html] view plain copy
  1. <bean id="propertyConfigurer1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  2. <property name="locations">  
  3.     <value>WEB-INF/classes/db.properties</value>  
  4. </property>  
  5.   </bean>  

为简化PropertyPlaceholderConfigurer的使用,Spring提供了<context:property-placeholder/>元素,像applicationContext.xml文件中这样:<context:property-placeholder location="classpath:db.properties" />
这里就很清楚了,我们只要继承PropertyPlaceholderConfigurer对象,重写PropertiesLoaderSupport接口的loadProperties方法,就可以对外部属性文件的属性值进行相关的操作了

明白了需求,下来开始我们的实现代码:

DecryptPropertyPlaceholderConfigurer.java

[java] view plain copy
  1. import java.io.File;  
  2. import java.io.FileOutputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.InputStreamReader;  
  6. import java.util.Properties;  
  7.    
  8. import org.apache.commons.lang.StringUtils;  
  9. import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;  
  10. import org.springframework.core.io.Resource;  
  11.    
  12. import com.huawei.smc.commons.constants.CommonContants;  
  13.    
  14. /** 
  15.  * <一句话功能简述> 
  16.  *  
  17.  * @author  hKF44803 
  18.  * @version  [版本号, 2011-12-6] 
  19.  * @see  [相关类/方法] 
  20.  * @since  [产品/模块版本] 
  21.  */  
  22. public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer  
  23. {  
  24.     private Resource[] locations;  
  25.        
  26.     private DecryptPropertiesPersister propertiesPersister = new DecryptPropertiesPersister();  
  27.        
  28.     private String fileEncoding = "utf-8";  
  29.        
  30.     private boolean ignoreResourceNotFound = false;  
  31.        
  32.     /** 
  33.      * {@inheritDoc} 
  34.      */  
  35.     @Override  
  36.     public void setLocations(Resource[] locations)  
  37.     {  
  38.         this.locations = locations;  
  39.     }  
  40.        
  41.     /** 
  42.      * {@inheritDoc} 
  43.      */  
  44.     @Override  
  45.     public void setFileEncoding(String encoding)  
  46.     {  
  47.         this.fileEncoding = encoding;  
  48.     }  
  49.        
  50.     /** 
  51.      * {@inheritDoc} 
  52.      */  
  53.     @Override  
  54.     public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound)  
  55.     {  
  56.         this.ignoreResourceNotFound = ignoreResourceNotFound;  
  57.     }  
  58.        
  59.     /** 
  60.      * {@inheritDoc} 
  61.      */  
  62.     @Override  
  63.     public void loadProperties(Properties props)  
  64.         throws IOException  
  65.     {  
  66.            
  67.         // 属性文件是否为空  
  68.         if (this.locations != null)  
  69.         {  
  70.             // 循环读取属性文件  
  71.             for (int i = 0; i < this.locations.length; i++)  
  72.             {  
  73.                 Resource location = this.locations[i];  
  74.                    
  75.                 InputStream is = null;  
  76.                 FileOutputStream fos = null;  
  77.                 try  
  78.                 {  
  79.                        
  80.                     is = location.getInputStream();  
  81.                        
  82.                     // 检查文件是否是XML文件  
  83.                     if (location.getFilename().endsWith(XML_FILE_EXTENSION))  
  84.                     {  
  85.                         this.propertiesPersister.loadFromXml(props, is);  
  86.                     }  
  87.                    // 属性文件  
  88.                     else  
  89.                     {  
  90.                         this.propertiesPersister.doLoad(props, new InputStreamReader(is, this.fileEncoding));  
  91.                         String content = this.propertiesPersister.getEncryptContent();  
  92.                            
  93.                         // 查找是否存在加密标识  
  94.                         if (StringUtils.contains(content, CommonContants.DECRYPT_FLAG))  
  95.                         {  
  96.                             try  
  97.                             {  
  98.                                 File file = location.getFile();  
  99.                                    
  100.                                 fos = new FileOutputStream(file);  
  101.                                    
  102.                                 fos.write(this.propertiesPersister.getEncryptContent().getBytes());  
  103.                                 fos.flush();  
  104.                                    
  105.                             }  
  106.                             finally  
  107.                             {  
  108.                                 if (null != fos)  
  109.                                 {  
  110.                                     fos.close();  
  111.                                 }  
  112.                             }  
  113.                         }  
  114.                     }  
  115.                 }  
  116.                 catch (IOException ex)  
  117.                 {  
  118.                     if (this.ignoreResourceNotFound)  
  119.                     {  
  120.                         if (logger.isWarnEnabled())  
  121.                         {  
  122.                             logger.warn("Could not load properties from " + location + ": " + ex.getMessage());  
  123.                         }  
  124.                     }  
  125.                     else  
  126.                     {  
  127.                         throw ex;  
  128.                     }  
  129.                 }  
  130.                 finally  
  131.                 {  
  132.                        
  133.                     if (is != null)  
  134.                     {  
  135.                         is.close();  
  136.                     }  
  137.                        
  138.                 }  
  139.             }  
  140.         }  
  141.     }  
  142. }  

其中propertiesPersister变量用我们写的DefaultPropertiesPersister类来实现,DecryptPropertiesPersister.java对象

[java] view plain copy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.Reader;  
  4. import java.util.Properties;  
  5.    
  6. import org.springframework.util.DefaultPropertiesPersister;  
  7. import org.springframework.util.StringUtils;  
  8.    
  9. import com.huawei.smc.commons.constants.CommonContants;  
  10. import com.huawei.smc.commons.constants.NumberConstants;  
  11. import com.huawei.smc.commons.util.ThreeDesUtil;  
  12.    
  13. /** 
  14.  * 重载DefaultPropertiesPersister类 
  15.  *  
  16.  * @author  hKF44803 
  17.  * @version  [版本号, 2011-12-6] 
  18.  * @see  [相关类/方法] 
  19.  * @since  [产品/模块版本] 
  20.  */  
  21. public class DecryptPropertiesPersister extends DefaultPropertiesPersister  
  22. {  
  23.     // 加密后的字符串  
  24.     private String encryptContent;  
  25.        
  26.     public String getEncryptContent()  
  27.     {  
  28.         return encryptContent;  
  29.     }  
  30.        
  31.     /** 
  32.      * {@inheritDoc} 
  33.      */  
  34.     @Override  
  35.     protected void doLoad(Properties props, Reader reader)  
  36.         throws IOException  
  37.     {  
  38.         BufferedReader in = new BufferedReader(reader);  
  39.            
  40.         // 最后写入的内容  
  41.         StringBuilder sbContent = new StringBuilder();  
  42.            
  43.         // 循环读取文件  
  44.         while (true)  
  45.         {  
  46.             // 读取每一行  
  47.             String line = in.readLine();  
  48.                
  49.             // 非空检查  
  50.             if (line == null)  
  51.             {  
  52.                 break;  
  53.             }  
  54.                
  55.             // 去掉空格  
  56.             line = StringUtils.trimLeadingWhitespace(line);  
  57.                
  58.             // 读取行为空,跳出循环  
  59.             if (line.length() == 0)  
  60.             {  
  61.                 // 长度为0,换行  
  62.                 sbContent.append(" ");  
  63.                    
  64.                 continue;  
  65.             }  
  66.                
  67.             // 每行的第一个字符  
  68.             char firstChar = line.charAt(0);  
  69.                
  70.             // 第一个字符不是#和!  
  71.             if (firstChar != '#' && firstChar != '!')  
  72.             {  
  73.                 while (endsWithContinuationMarker(line))  
  74.                 {  
  75.                     String nextLine = in.readLine();  
  76.                     line = line.substring(0, line.length() - 1);  
  77.                        
  78.                     // 非空检查  
  79.                     if (nextLine != null)  
  80.                     {  
  81.                         line += StringUtils.trimLeadingWhitespace(nextLine);  
  82.                     }  
  83.                 }  
  84.                    
  85.                 // 查找等号所有位置的索引  
  86.                 int separatorIndex = line.indexOf("=");  
  87.                    
  88.                 // 没有等号  
  89.                 if (separatorIndex == -1)  
  90.                 {  
  91.                     separatorIndex = line.indexOf(":");  
  92.                 }  
  93.                    
  94.                 // 取KEY  
  95.                 String key = (separatorIndex != -1) ? line.substring(0, separatorIndex) : line;  
  96.                    
  97.                 // 取KEY的值  
  98.                 String value = (separatorIndex != -1) ? line.substring(separatorIndex + 1) : "";  
  99.                    
  100.                 // 去掉空格  
  101.                 key = StringUtils.trimTrailingWhitespace(key);  
  102.                 value = StringUtils.trimLeadingWhitespace(value);  
  103.                    
  104.                 // 将所有的属性放到持久的属性集*  
  105.                 props.put(unescape(key), unescape(value));  
  106.                    
  107.   // DB属性文件  
  108.                 if (CommonContants.DB_PASSWORD_PROPS.equals(key))  
  109.                 {  
  110.                     // 实例加密工具类  
  111.                     ThreeDesUtil desUtil = new ThreeDesUtil();  
  112.                        
  113.                     // DB密码解密  
  114.                     if (value.startsWith(CommonContants.DECRYPT_FLAG))  
  115.                     {  
  116.                         // 去掉标识  
  117.                         value = value.substring(NumberConstants.INT_5);  
  118.                            
  119.                         // 对加密的属性进行3DES解密  
  120.                         value = desUtil.decrypt(value);  
  121.                            
  122.                         // 解密的值放到props中  
  123.                         props.put(unescape(key), unescape(value));  
  124.                     }  
  125.                     // DB密码加密  
  126.                     else  
  127.                     {  
  128.                         // 加密指定的值  
  129.                         String strEncrypt = desUtil.encrypt(value);  
  130.                            
  131.                         // 加密后的值添加一个标识,区分解密、加密  
  132.                         value = CommonContants.DECRYPT_FLAG + strEncrypt;  
  133.                            
  134.                         // 加密后的行  
  135.                         line = key + CommonContants.PROPERTIES_SEPERATE + value;  
  136.                            
  137.                         sbContent.append(line + " ");  
  138.                     }  
  139.                 }  
  140.                 // 追加其它的属性  
  141.                 else  
  142.                 {  
  143.                     sbContent.append(line + " ");  
  144.                 }  
  145.             }  
  146.             else  
  147.             {  
  148.                 // 追加读取的注释内容  
  149.                 sbContent.append(line + " ");  
  150.             }  
  151.         }  
  152.            
  153.         encryptContent = sbContent.toString();  
  154.     }  
  155. }  

最后需要修改下applicationContext.xml文件,如下:

[java] view plain copy
  1. <bean id="propertyConfigurer1" class="com.huawei.smc.commons.DecryptPropertyPlaceholderConfigurer">  
  2. <property name="locations">  
  3.     <value>WEB-INF/classes/db.properties</value>  
  4. </property>  
  5.     </bean>  
  6.    
  7.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  8. <property name="driverClass" value="${db.driver}" />  
  9. <property name="jdbcUrl" value="${db.url}" />  
  10. <property name="user" value="${db.username}" />  
  11. <property name="password" value="${db.password}" />  
  12. <property name="checkoutTimeout" value="3000" />  
  13.    </bean>  
Spring对外部属性文件指定的某个属性进行加密、解密第1张

这样对属性的加密就完成了,Spring进行加载的完成后,属性就加密了

提示:如果在配置中有多个配置文件需要加载,并且这些属性文件不需要做任何处理,那就需要添加下面的配置:

[java] view plain copy
  1. <bean id="propertyConfigurer"  
  2. class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  
  3. <property name="order" value="1" />    
  4.         <property name="ignoreUnresolvablePlaceholders" value="true" />  
  5. <property name="locations">  
  6. <value>WEB-INF/classes/smc.properties</value>  
  7. </property>  

免责声明:文章转载自《Spring对外部属性文件指定的某个属性进行加密、解密》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DBNull 与 求和方法Android provider中使用sqlite内存数据库下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

前端框架Vue自学之Vue router(六)

终极目标:掌握和使用Vue(全家桶:Core+Vue-router+Vuex) 本博客目的:记录Vue学习的进度和心得(Vue router) 内容:学习和使用Vue router。 正文: Vue router 一、认识路由 1、路由(routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。 2、路由器提供了两种机制:路由和转送。路由是决定...

Oracle 数据文件 reuse 属性 说明(转载)

Oracle 表空间 创建参数 说明 http://blog.csdn.net/tianlesoftware/archive/2011/01/27/6166928.aspx 当我们对表空间添加数据文件的时候,有一个reuse 属性。 10g的官网对这个参数的说明如下: REUSE Specify REUSE to allow Oracle to reuse...

写一个工具生成数据库实体类

写一个java工具从数据库生成实体类 开始写之前的说明 ​ 这只是一个简单的解析数据库建表语句之后,根据解析结果生成java文件的一个简单工具。写的原因有两个。 ​ 1:项目中没有人写实体类的注释,字段的注释,现有的工具也没法根据数据库的注释自动添加到class文件上。 ​ 2:自己写一个也似乎不是很难。 ​ 所以就自己写了一个。 这里在生成java文件的...

优秀开源项目的svn地址

很多优秀的开源项目已经提供SVN源码签出了,无论是解疑还是学习,都是一大幸福之事啊! Apache的SVN库,强烈推荐! http://svn.apache.org/repos/asf/  里面不但有Struts的源码,还有著名的Apache jakarta project 相当好的Web UI框架Tiles 现在很流行的项目管理工具Maven Ant、C...

SpringCloud是什么?

参考链接: http://blog.csdn.net/forezp/article/details/70148833 一、概念定义 Spring Cloud是一个微服务框架,相比Dubbo等RPC框架,Spring Cloud提供的全套的分布式系统解决方案。 Spring Cloud对微服务基础框架Netflix的多个开源组件进行了封装,同时又实现了和云端...

SpringBoot集成多数据源

一、多数据源集成简介 多数据源集成使用dynamic-sring-boot-starter,github地址:https://github.com/baomidou/dynamic-datasource-spring-boot-starter,详细使用可以参考github地址。 二、BDP框架集成多数据源步骤 1、pom.xm文件引入dynamic-dat...