您的位置:首页 > 编程语言 > Java开发

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

2012-03-08 18:18 471 查看
在我们开发当中,经常会用到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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: