场景:spring项目中,为了安全,配置文件中的部分信息设置成密文的,比如数据库密码,spring在加载这些配置文件时,需要指定相应的解密算法去解密这些配置项。
实现原理:
1.继承spring中的org.springframework.beans.factory.config.PropertyPlaceholderConfigurer类,并覆盖其中的loadProperties方法,同时需要声明locations属性,该属性是需要加载的配置文件的路径,且该属性在PropertyPlaceholderConfigurer类中定义,这里需要把它给覆盖,同时提供相应的覆盖后的set 和get 方法。
package com.jaycn.util; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.Properties; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.core.io.AbstractFileResolvingResource; import org.springframework.core.io.Resource; /** * 加载properties文件的入口 * @Filename PropertiesLoad.java * * @Description * * @Version 1.0 * * @Author MEISEI * * @Email * * @History *<li>Author: MEISEI</li> *<li>Date: 2013-5-30</li> *<li>Version: 1.0</li> *<li>Content: create</li> * */ public class PropertiesLoad extends PropertyPlaceholderConfigurer { /** Comment for <code>propertiesPersister</code> * 处理properties文件的对象 * */ private PropertiesProcessor propertiesPersister = new PropertiesProcessor(); private Resource[] locations; @Override public void setLocation(Resource location) { this.locations = new Resource[] { location }; } @Override public void setLocations(Resource[] locations) { this.locations = locations; } @Override protected void loadProperties(Properties props) throws IOException { if (this.locations != null) { InputStream is = null; Reader reader = null; String filename = null; for (Resource location : this.locations) { is = null; try { is = location.getInputStream(); reader = new InputStreamReader(is); filename = (location instanceof AbstractFileResolvingResource) ? location .getFilename() : null; if ((filename != null) && (filename.endsWith(".xml"))) { this.propertiesPersister.loadFromXml(props, is); } else { this.propertiesPersister.doLoad(props, reader); } } catch (IOException ex) { System.out.println(""); } finally { if (is != null) is.close(); } } } } }
2.继承spring中的org.springframework.util.DefaultPropertiesPersister类,覆盖其中的doLoad方法,配置项的解密过程在doLoad方法中实现。
package com.jaycn.util; import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; import java.util.ArrayList; import java.util.List; import java.util.Properties; import org.springframework.util.DefaultPropertiesPersister; import org.springframework.util.StringUtils; /** * 处理properties文件,并将数据库密文转换为明文 * @Filename PropertiesProcessor.java * * @Description * * @Version 1.0 * * @Author MEISEI * * @Email * * @History *<li>Author: MEISEI</li> *<li>Date: 2013-5-30</li> *<li>Version: 1.0</li> *<li>Content: create</li> * */ public class PropertiesProcessor extends DefaultPropertiesPersister { /** Comment for <code>decodeKeys</code> * 存放properties文件中的密文项 * */ private static List<String> DEC_VALUE_LIST = new ArrayList<String>(); static { DEC_VALUE_LIST.add("jdbc.password"); } @Override protected void doLoad(Properties props, Reader reader) throws IOException { BufferedReader in = new BufferedReader(reader); String line = null; String nextLine = null; char firstChar = 0; while (true) { line = in.readLine(); if (line == null) { return; } line = StringUtils.trimLeadingWhitespace(line); if (line.length() > 0) { firstChar = line.charAt(0); if ((firstChar != '#') && (firstChar != '!')) { while (endsWithContinuationMarker(line)) { nextLine = in.readLine(); line = line.substring(0, line.length() - 1); if (nextLine != null) { line = line + StringUtils.trimLeadingWhitespace(nextLine); } } int separatorIndex = line.indexOf("="); if (separatorIndex == -1) { separatorIndex = line.indexOf(":"); } String key = separatorIndex != -1 ? line.substring(0, separatorIndex) : line; String value = separatorIndex != -1 ? line.substring(separatorIndex + 1) : ""; key = StringUtils.trimTrailingWhitespace(key); value = StringUtils.trimLeadingWhitespace(value); if (DEC_VALUE_LIST.contains(key)) { // ******用相应的解密算法去解密配置项****** value = decodeValue(value); } props.put(unescape(key), unescape(value)); } } } } /** * 使用解密算法对value进行解密 * @param value 加密的value * @return 解密的value */ private String decodeValue(String value) { return DesEncryptDecrypt.decode(value); } }
3.在spring的配置文件中,配置读取properties文件的解析类。比如在applicationContext.xml中配置如下信息:
<!-- JDBC参数配置 --> <bean class="com.jaycn.util.PropertiesLoad"> <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" /> <property name="ignoreResourceNotFound" value="true" /> <property name="locations"> <list> <value>classpath:db.properties</value> </list> </property> </bean>
注意:数据库配置文件的读取应该放在配置数据库连接信息之前,不然数据库的密码为密文会导致无法连接到数据库。