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

在我们开发当中,经常会用到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>


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值