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文件
- <context:property-placeholder location="classpath:db.properties" />
- <bean id="dataSource"
- class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass" value="${db.driver}" />
- <property name="jdbcUrl" value="${db.url}" />
- <property name="user" value="${db.username}" />
- <property name="password" value="${db.password}" />
- <property name="checkoutTimeout" value="3000" />
- </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/>中的属性值进行外在化管理。
就像这样:
- <bean id="propertyConfigurer1" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="locations">
- <value>WEB-INF/classes/db.properties</value>
- </property>
- </bean>
为简化PropertyPlaceholderConfigurer的使用,Spring提供了<context:property-placeholder/>元素,像applicationContext.xml文件中这样:<context:property-placeholder
location="classpath:db.properties" />
这里就很清楚了,我们只要继承PropertyPlaceholderConfigurer对象,重写PropertiesLoaderSupport接口的loadProperties方法,就可以对外部属性文件的属性值进行相关的操作了
明白了需求,下来开始我们的实现代码:
DecryptPropertyPlaceholderConfigurer.java
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.util.Properties;
- import org.apache.commons.lang.StringUtils;
- import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
- import org.springframework.core.io.Resource;
- import com.huawei.smc.commons.constants.CommonContants;
- /**
- * <一句话功能简述>
- *
- * @author hKF44803
- * @version [版本号, 2011-12-6]
- * @see [相关类/方法]
- * @since [产品/模块版本]
- */
- public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer
- {
- private Resource[] locations;
- private DecryptPropertiesPersister propertiesPersister = new DecryptPropertiesPersister();
- private String fileEncoding = "utf-8";
- private boolean ignoreResourceNotFound = false;
- /**
- * {@inheritDoc}
- */
- @Override
- public void setLocations(Resource[] locations)
- {
- this.locations = locations;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public void setFileEncoding(String encoding)
- {
- this.fileEncoding = encoding;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound)
- {
- this.ignoreResourceNotFound = ignoreResourceNotFound;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- public 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;
- FileOutputStream fos = null;
- try
- {
- is = location.getInputStream();
- // 检查文件是否是XML文件
- if (location.getFilename().endsWith(XML_FILE_EXTENSION))
- {
- this.propertiesPersister.loadFromXml(props, is);
- }
- // 属性文件
- else
- {
- this.propertiesPersister.doLoad(props, new InputStreamReader(is, this.fileEncoding));
- String content = this.propertiesPersister.getEncryptContent();
- // 查找是否存在加密标识
- if (StringUtils.contains(content, CommonContants.DECRYPT_FLAG))
- {
- try
- {
- File file = location.getFile();
- fos = new FileOutputStream(file);
- fos.write(this.propertiesPersister.getEncryptContent().getBytes());
- fos.flush();
- }
- finally
- {
- if (null != fos)
- {
- fos.close();
- }
- }
- }
- }
- }
- 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();
- }
- }
- }
- }
- }
- }
其中propertiesPersister变量用我们写的DefaultPropertiesPersister类来实现,DecryptPropertiesPersister.java对象
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.Reader;
- import java.util.Properties;
- import org.springframework.util.DefaultPropertiesPersister;
- import org.springframework.util.StringUtils;
- import com.huawei.smc.commons.constants.CommonContants;
- import com.huawei.smc.commons.constants.NumberConstants;
- import com.huawei.smc.commons.util.ThreeDesUtil;
- /**
- * 重载DefaultPropertiesPersister类
- *
- * @author hKF44803
- * @version [版本号, 2011-12-6]
- * @see [相关类/方法]
- * @since [产品/模块版本]
- */
- public class DecryptPropertiesPersister extends DefaultPropertiesPersister
- {
- // 加密后的字符串
- private String encryptContent;
- public String getEncryptContent()
- {
- return encryptContent;
- }
- /**
- * {@inheritDoc}
- */
- @Override
- protected void doLoad(Properties props, Reader reader)
- throws IOException
- {
- BufferedReader in = new BufferedReader(reader);
- // 最后写入的内容
- StringBuilder sbContent = new StringBuilder();
- // 循环读取文件
- while (true)
- {
- // 读取每一行
- String line = in.readLine();
- // 非空检查
- if (line == null)
- {
- break;
- }
- // 去掉空格
- line = StringUtils.trimLeadingWhitespace(line);
- // 读取行为空,跳出循环
- if (line.length() == 0)
- {
- // 长度为0,换行
- sbContent.append("\n");
- continue;
- }
- // 每行的第一个字符
- char firstChar = line.charAt(0);
- // 第一个字符不是#和!
- if (firstChar != '#' && firstChar != '!')
- {
- while (endsWithContinuationMarker(line))
- {
- String nextLine = in.readLine();
- line = line.substring(0, line.length() - 1);
- // 非空检查
- if (nextLine != null)
- {
- line += StringUtils.trimLeadingWhitespace(nextLine);
- }
- }
- // 查找等号所有位置的索引
- int separatorIndex = line.indexOf("=");
- // 没有等号
- if (separatorIndex == -1)
- {
- separatorIndex = line.indexOf(":");
- }
- // 取KEY
- String key = (separatorIndex != -1) ? line.substring(0, separatorIndex) : line;
- // 取KEY的值
- String value = (separatorIndex != -1) ? line.substring(separatorIndex + 1) : "";
- // 去掉空格
- key = StringUtils.trimTrailingWhitespace(key);
- value = StringUtils.trimLeadingWhitespace(value);
- // 将所有的属性放到持久的属性集*
- props.put(unescape(key), unescape(value));
- // DB属性文件
- if (CommonContants.DB_PASSWORD_PROPS.equals(key))
- {
- // 实例加密工具类
- ThreeDesUtil desUtil = new ThreeDesUtil();
- // DB密码解密
- if (value.startsWith(CommonContants.DECRYPT_FLAG))
- {
- // 去掉标识
- value = value.substring(NumberConstants.INT_5);
- // 对加密的属性进行3DES解密
- value = desUtil.decrypt(value);
- // 解密的值放到props中
- props.put(unescape(key), unescape(value));
- }
- // DB密码加密
- else
- {
- // 加密指定的值
- String strEncrypt = desUtil.encrypt(value);
- // 加密后的值添加一个标识,区分解密、加密
- value = CommonContants.DECRYPT_FLAG + strEncrypt;
- // 加密后的行
- line = key + CommonContants.PROPERTIES_SEPERATE + value;
- sbContent.append(line + "\n");
- }
- }
- // 追加其它的属性
- else
- {
- sbContent.append(line + "\n");
- }
- }
- else
- {
- // 追加读取的注释内容
- sbContent.append(line + "\n");
- }
- }
- encryptContent = sbContent.toString();
- }
- }
最后需要修改下applicationContext.xml文件,如下:
- <bean id="propertyConfigurer1" class="com.huawei.smc.commons.DecryptPropertyPlaceholderConfigurer">
- <property name="locations">
- <value>WEB-INF/classes/db.properties</value>
- </property>
- </bean>
- <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
- <property name="driverClass" value="${db.driver}" />
- <property name="jdbcUrl" value="${db.url}" />
- <property name="user" value="${db.username}" />
- <property name="password" value="${db.password}" />
- <property name="checkoutTimeout" value="3000" />
- </bean>

这样对属性的加密就完成了,Spring进行加载的完成后,属性就加密了
提示:如果在配置中有多个配置文件需要加载,并且这些属性文件不需要做任何处理,那就需要添加下面的配置:
- <bean id="propertyConfigurer"
- class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
- <property name="order" value="1" />
- <property name="ignoreUnresolvablePlaceholders" value="true" />
- <property name="locations">
- <value>WEB-INF/classes/smc.properties</value>
- </property>
Spring对外部属性文件指定的某个属性进行加密、解密的更多相关文章
- Java - 得到项目中properties属性文件中定义的属性值
public static String getPropertiesValue(String fileName, String key) { return ResourceBundle.getBu ...
- Java属性中指定Json的属性名称
只需要使用注解"@JsonProperty(value = "pwd")" import com.fasterxml.jackson.annotation.Js ...
- 属性文件——Java&Spring
属性文件 什么是属性文件 ? 定义:一个扩展名为properties文件,属性文件都是以key-value(键值对)来保存文件的内容,如:log4j.properties,db.properties等 ...
- 十八 Spring的JDBC模板:引入外部属性文件
配置外部属性文件 配置文件里引入属性文件,两种方式 第一种: 第二种: 引入属性文件的值: 测试: <?xml version="1.0" encoding="UT ...
- java解析属性文件
-----------------------解析属性文件----------------------------- /** * 获取src下属性文件 * @param params * ...
- Java操作属性文件,支持新增或更新多个属性
Java操作属性文件.支持新增或更新多个属性 一.更新或新增单个属性的方法 /** * 写入properties信息 * @param filePath 绝对路径(包含文件名称和后缀名) * @par ...
- java:Properties属性文件概念
java:Properties属性文件概念 在java之前的国际化程序中提出了一个属性文件的概念,属性文件的后缀是:*.properties,那么在java中提供了意个属性文件的专门操作类,Prope ...
- Java AES加密解密工具 -- GUI 、在线传输文件
原理 对于任意长度的明文,AES首先对其进行分组,每组的长度为128位.分组之后将分别对每个128位的明文分组进行加密. 对于每个128位长度的明文分组的加密过程如下: (1)将128位AES ...
- [原创]java WEB学习笔记99:Spring学习---Spring Bean配置:自动装配,配置bean之间的关系(继承/依赖),bean的作用域(singleton,prototype,web环境作用域),使用外部属性文件
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...
随机推荐
- jqgrid控件列分组
<%-- builed by manage.aspx.cmt [ver:2014.48.11] at 2014/10/11 16:48:33 --%> <%@ Page Langua ...
- YDNJS(上卷):this 的绑定对象
函数中的 this 是在调用时被绑定的,this 指向谁完全取决于函数的调用位置. 确定 this 的绑定对象的方式有 4 种. 默认绑定 默认绑定就是将函数中的 this 绑定给了全局对象 wind ...
- DFS小题
原创 题目为:()()()+()()()=()()() 将1~9这9个数字填入括号,每个数字只能用一次. 枚举: public class Test { public static void main ...
- 关于JAVA数组的几点注意事项与一些低级错误
1.数组不是集合,它只能保存同种类型的多个原始类型或者对象的引用.数组保存的仅仅是对象的引用,而不是对象本身. 2.数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型 ...
- [.net 多线程]volatile 摘录
一.volatile 介绍 volatile 关键字指示一个字段可以由多个同时执行的线程修改. 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制. 这样可以确保该字段在任何时 ...
- Markdown使用心得
1. 标题的使用 在使用标题时,如果为了层次清晰,可以在"#"后加上"1. "或者"1.1. "这种序号. 每一级标题的正文结束后,最好加一 ...
- 搭建基于MinGW平台的《OpenGL蓝皮书(OpenGL SuperBibe 5th)》示例代码编译环境
副标题:搭建基于MinGW平台的<OpenGL超级宝典>(OpenGL蓝皮书第5版)GLTools 编译环境.示例代码:Triangle.cpp @ SB5.zip 以下内容以及方法均参考 ...
- wx-xcx
一.因测试小程序,搜索了各种测试小程序的注意点,为方便后续查看,暂时整理罗列如下: 1.留意分享功能的权限:如果某页面对游客访问有权限限制,则需要设置取消其分享功能. 2.小程序强制更新功能:新版小程 ...
- leecode刷题(4)-- 存在重复数组
leecode刷题(4)-- 存在重复数组 存在重复数组 题目描述: 给定一个整数数组,判断是否存在重复元素. 如果任何值在数组中出现至少两次,函数返回 true.如果数组中每个元素都不相同,则返回 ...
- BZOJ2668:[CQOI2012]交换棋子(费用流)
题目描述 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. 输入输出格式 输入格式: 第一行 ...