[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. 在线创建MongoDB免费集群(MangoDB Atlas)

    MongoDB Atlas是MongoDB的云服务,构建在亚马逊的AWS上,MongoDB允许用户在上面创建一个免费集群作为学习使用. 1. 注册MongoDB cloud账号: 访问www.mong ...

  2. Bean管理注解的例子

  3. Linq学习<一>

    lambda查询语法: var result =arrarylist.where(n=>n.contains("l"))  简化的委托方法实例 linq查询结构: var  ...

  4. 我用Django搭网站(3)-表单RSA加密

    之前开发项目时因为种种原因一直使用明文提交,表单直接明文提交非常不安全,只要稍加操作就能轻易获取用户的信息.在众里寻他千百度之后决定使用RSA加密方式,简单可靠. 项目准备 一.安装PyCrypto库 ...

  5. Android ViewPager + Fragment的布局

    ViewPager And Fragment 1.之前有篇博客是讲ViewPager的用法的:http://www.cnblogs.com/liangstudyhome/p/3773156.html ...

  6. ecliplse集成SVN

    按照下图操作 : SVN不同版本下载链接在文章底部有提供 上图点击add之后稍等一会就会弹出下图: 上图点击next之后: 最后等待完成之后重启ecliplse即可 重启ecliplse之后显示SVN ...

  7. C# 继承(3)

    接上章代码: class NameList { public NameList() => Console.WriteLine("这个是NameList的构造函数"); ~Na ...

  8. Jmeter的主要元件及元件的执行顺序

    一.JMeter的主要元素 1.Test Pan(测试计划) Jmeter中的测试计划包括一个或者一组的测试用例,一个Thread Group可以认为是一个测试用例,一个测试计划中可以包括多个Thre ...

  9. char *p="abc" 与 char p[]="abc" 的区别

    本文来源于网络 出处:点我 有这样一段代码: #include "stdio.h" char *get_string_1() { char p[] = "hello wo ...

  10. 简单使用postman

    一.get请求 获取学生信息接口文档内容: 简要描述: 获取学生信息接口 请求URL: http://ip/api/user/stu_info 请求方式: get 参数: 参数名 必选 类型 说明 s ...