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


    [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进行加载的完成后,属性就加密了

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

    [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>  

  • 相关阅读:
    [持续交付实践] Jenkins 中国用户大会参会见闻
    [持续交付实践] 研发协作平台:从交付流水线到研发协作平台
    [持续交付实践] pipeline使用:Shared Libraries
    [持续交付实践] 交付流水线设计:分层自动化测试
    [持续交付实践] 交付流水线设计:安全专项测试
    openstack手动安装
    python threading模块2
    socket粗解
    python实现Telnet远程登陆到设备并执行命令
    Shell脚本学习
  • 原文地址:https://www.cnblogs.com/pekkle/p/6568662.html
Copyright © 2020-2023  润新知