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

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

最后需要修改下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. <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
  7. <property name="driverClass" value="${db.driver}" />
  8. <property name="jdbcUrl" value="${db.url}" />
  9. <property name="user" value="${db.username}" />
  10. <property name="password" value="${db.password}" />
  11. <property name="checkoutTimeout" value="3000" />
  12. </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>

Spring对外部属性文件指定的某个属性进行加密、解密的更多相关文章

  1. Java - 得到项目中properties属性文件中定义的属性值

    public static String getPropertiesValue(String fileName, String key) {   return ResourceBundle.getBu ...

  2. Java属性中指定Json的属性名称

    只需要使用注解"@JsonProperty(value = "pwd")" import com.fasterxml.jackson.annotation.Js ...

  3. 属性文件——Java&Spring

    属性文件 什么是属性文件 ? 定义:一个扩展名为properties文件,属性文件都是以key-value(键值对)来保存文件的内容,如:log4j.properties,db.properties等 ...

  4. 十八 Spring的JDBC模板:引入外部属性文件

    配置外部属性文件 配置文件里引入属性文件,两种方式 第一种: 第二种: 引入属性文件的值: 测试: <?xml version="1.0" encoding="UT ...

  5. java解析属性文件

    -----------------------解析属性文件----------------------------- /**   * 获取src下属性文件   * @param params   * ...

  6. Java操作属性文件,支持新增或更新多个属性

    Java操作属性文件.支持新增或更新多个属性 一.更新或新增单个属性的方法 /** * 写入properties信息 * @param filePath 绝对路径(包含文件名称和后缀名) * @par ...

  7. java:Properties属性文件概念

    java:Properties属性文件概念 在java之前的国际化程序中提出了一个属性文件的概念,属性文件的后缀是:*.properties,那么在java中提供了意个属性文件的专门操作类,Prope ...

  8. Java AES加密解密工具 -- GUI 、在线传输文件

    原理 对于任意长度的明文,AES首先对其进行分组,每组的长度为128位.分组之后将分别对每个128位的明文分组进行加密. 对于每个128位长度的明文分组的加密过程如下:     (1)将128位AES ...

  9. [原创]java WEB学习笔记99:Spring学习---Spring Bean配置:自动装配,配置bean之间的关系(继承/依赖),bean的作用域(singleton,prototype,web环境作用域),使用外部属性文件

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

随机推荐

  1. adb shell unauthorized问题

    出现unauthorized 一般插上usb后,手机会弹出一个要求你授权debugging的对话框,如果没有的话,就是rsa_key有问题: /adb_keys. User-installed key ...

  2. linq to object 未完待续

    1.linq to string string s2 = "abc"; var data2 = s2.Where(x => x.CompareTo('a') > 0). ...

  3. Filter过滤器简单学习

    Servlet 过滤器方法 过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类.javax.servlet.Filter 接口定义了三个方法: 序号 方法 & ...

  4. [GO]指针和函数配合的值传递

    package main import "fmt" func swap(a, b int) { a, b = b, a fmt.Printf("a = %d, b = % ...

  5. 编写高质量代码改善C#程序的157个建议——建议50:在Dispose模式中应区别对待托管资源和非托管资源

    建议50:在Dispose模式中应区别对待托管资源和非托管资源 真正资源释放代码的那个虚方法是带一个bool参数的,带这个参数,是因为我们在资源释放时要区别对待托管资源和非托管资源. 提供给调用者调用 ...

  6. Android Studio2.3更换默认的ConstraintLayout布局

    1.在as安装目录\plugins\Android\lib\templates\activities\common\root\res\layout下,找到simple.xml.ftl文件 2.用以下布 ...

  7. HackTwo

    使用延迟加载以及避免代码重复 ​一.概要:     <include />标签是整理布局的有效工具,提供了合理组织XML布局文件的有效方法.     ViewStub是实现延迟加载视图的优 ...

  8. 【转】.net 在线播放各类视频

    原文地址:http://blog.csdn.net/hefeng_aspnet/article/details/9704857 一.后台拼字符串动态加载写法 前台调用代码 <!DOCTYPE h ...

  9. [raspberry p3] suse wifi驱动加载

    问题 raspberry pi3安装后发现wifi 启动不了, brcmf_sdio加载失败了,return error code为-110 处理方法 打开 /etc/dracut.conf.d/ra ...

  10. windows phone之依赖属性(DependencyProperty)

    Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能,这些服务通常统称为 WPF 属性系统.由 WPF ...