Spring中扩展 PropertyPlaceholderConfigurer处理加密属性文件 - 一条宝鱼的专栏 - CSDN博客

Spring中扩展 PropertyPlaceholderConfigurer处理加密属性文件

2014年01月07日 10:53:22 一条宝鱼 阅读数:3965 标签: java spring 加密 更多
个人分类: spring

当我们在项目中配置数据源时,经常会将其对应的一些属性值写到另外的属性文件中,这样的好处是可以简化项目维护和部署工作,当项目从开发环境迁移到生产环境的时候,运维人员只需要修改数据源对应的属性文件就可以了,无需关注其他的配置文件。如果在属性文件中将数据库的用户名和密码等敏感信息以明文的方式写在文件中,这是非常不安全的,所以我们就需要将属性文件中的部分信息进行加密处理以提高安全性。下面介绍如何运用spring中的PropertyPlaceholderConfigurer类对加密的属性值进行处理。

假设数据源配置信息放在jdbc.properties文件中,如果对属性文件不需要做任何处理则可以利用context命名空间定义属性文件:

  1. <context:property-placeholder location="classpath:jdbc.properties"/>
<context:property-placeholder location="classpath:jdbc.properties"/>

PropertyPlaceholderConfigurer类本身对加密的数据不做任何处理的,所以我们需要自定义类继承PropertyPlaceholderConfigurer,并且重写父类中的方法。通过spring源码我们可以看到PropertyPlaceholderConfigurer类uml图如下所示。


图(1)

PropertyResourceConfigurer类实现BeanFactoryPostProcessor接口是一个后置处理器类,找到postProcessBeanFactory方法发现跟获取属性值有关的方法为convertProperties方法,源码如下所示:

  1. protected void convertProperties(Properties props) {
  2. for(Enumeration propertyNames = props.propertyNames(); propertyNames.hasMoreElements();){
  3. String propertyName = (String) propertyNames.nextElement();
  4. String propertyValue = props.getProperty(propertyName);
  5. String convertedValue = convertProperty(propertyName, propertyValue);
  6. if(!ObjectUtils.nullSafeEquals(propertyValue, convertedValue))
  7. props.setProperty(propertyName, convertedValue);
  8. }
  9. }
  10. protected String convertProperty(String propertyName,String propertyValue) {
  11. return convertPropertyValue(propertyValue);
  12. }
  13. protected String convertPropertyValue(String originalValue) {
  14. return originalValue;
  15. }
  1. 1
    protected void convertProperties(Properties props) {
  2. 2
    for(Enumeration propertyNames = props.propertyNames(); propertyNames.hasMoreElements();){
  3. 3
    String propertyName = (String) propertyNames.nextElement();
  4. 4
    String propertyValue = props.getProperty(propertyName);
  5. 5
    String convertedValue = convertProperty(propertyName, propertyValue);
  6. 6
    if(!ObjectUtils.nullSafeEquals(propertyValue, convertedValue))
  7. 7
    props.setProperty(propertyName, convertedValue);
  8. 8
    }
  9. 9
    }
  10. 10
  11. 11
    protected String convertProperty(String propertyName,String propertyValue) {
  12. 12
    return convertPropertyValue(propertyValue);
  13. 13
    }
  14. 14
  15. 15
    protected String convertPropertyValue(String originalValue) {
  16. 16
    return originalValue;
  17. 17
    }

看到这里我们就可以清楚的知道spring是通过该类中的convertProperty方法返回对应属性名处理后的属性值的,convertProperty方法中调用了convertPropertyValue方法用于返回属性值,所以如果我们要对属性文件中的所有属性值进行解密处理,就只需重写convertPropertyValue方法, 这里我们只需针对指定的属性名做解密处理,所以在子类中重写convertProperty方法就可以了,子类代码如下所示:

  1. package com.yao.cs.common.config;
  2. import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
  3. import com.yao.cs.common.utils.DesUtils;
  4. public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
  5. /**
  6. * 重写父类方法,解密指定属性名对应的属性值
  7. */
  8. @Override
  9. protected String convertProperty(String propertyName,String propertyValue){
  10. if(isEncryptPropertyVal(propertyName)){
  11. return DesUtils.getDecryptString(propertyValue);//调用解密方法
  12. }else{
  13. return propertyValue;
  14. }
  15. }
  16. /**
  17. * 判断属性值是否需要解密,这里我约定需要解密的属性名用encrypt开头
  18. * @param propertyName
  19. * @return
  20. */
  21. private boolean isEncryptPropertyVal(String propertyName){
  22. if(propertyName.startsWith("encrypt")){
  23. return true;
  24. }else{
  25. return false;
  26. }
  27. }
  28. }
  1. 1
    package com.yao.cs.common.config;
  2. 2
    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
  3. 3
    import com.yao.cs.common.utils.DesUtils;
  4. 4
  5. 5
    public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{
  6. 6
    /**
  7. 7
    * 重写父类方法,解密指定属性名对应的属性值
  8. 8
    */
  9. 9
    @Override
  10. 10
    protected String convertProperty(String propertyName,String propertyValue){
  11. 11
    if(isEncryptPropertyVal(propertyName)){
  12. 12
    return DesUtils.getDecryptString(propertyValue);//调用解密方法
  13. 13
    }else{
  14. 14
    return propertyValue;
  15. 15
    }
  16. 16
    }
  17. 17
    /**
  18. 18
    * 判断属性值是否需要解密,这里我约定需要解密的属性名用encrypt开头
  19. 19
    * @param propertyName
  20. 20
    * @return
  21. 21
    */
  22. 22
    private boolean isEncryptPropertyVal(String propertyName){
  23. 23
    if(propertyName.startsWith("encrypt")){
  24. 24
    return true;
  25. 25
    }else{
  26. 26
    return false;
  27. 27
    }
  28. 28
    }
  29. 29
    }
复制

在spring容器中定义DecryptPropertyPlaceholderConfigurerbean,并且指定属性文件,如下所示:

  1. <bean class="com.nankang.cs.common.config.DecryptPropertyPlaceholderConfigurer"
  2. p:location="classpath:jdbc.properties"/>
  1. 1
    <bean class="com.nankang.cs.common.config.DecryptPropertyPlaceholderConfigurer"
  2. 2
    p:location="classpath:jdbc.properties"/>

jdbc.properties属性文件中的描述如下(需要解密的属性名约定以encrypt开头):

  1. encrypt_user=HSyMvmpqUdE\=
  2. encrypt_pwd=K/5gIGiWG+12csSxj8AINw==
encrypt_user=HSyMvmpqUdE\=
encrypt_pwd=K/5gIGiWG+12csSxj8AINw==

数据源如下配置:

  1. <bean id="firstDataSource" class="com.alibaba.druid.pool.DruidDataSource"
  2. destroy-method="close"
  3. p:driverClassName="${driverClassName}"
  4. p:url="${jdbcUrl}"
  5. p:username="${encrypt_user}"
  6. p:password="${encrypt_pwd}"
  7. p:initialSize="${initialSize}"
  8. p:maxActive="${maxActive}"
  9. p:minIdle="${minIdle}"
  10. p:poolPreparedStatements="${poolPreparedStatements}"
  11. p:testOnBorrow="${testOnBorrow}"/>
  1. 1
    <bean id="firstDataSource" class="com.alibaba.druid.pool.DruidDataSource"
  2. 2
    destroy-method="close"
  3. 3
    p:driverClassName="${driverClassName}"
  4. 4
    p:url="${jdbcUrl}"
  5. 5
    p:username="${encrypt_user}"
  6. 6
    p:password="${encrypt_pwd}"
  7. 7
    p:initialSize="${initialSize}"
  8. 8
    p:maxActive="${maxActive}"
  9. 9
    p:minIdle="${minIdle}"
  10. 10
    p:poolPreparedStatements="${poolPreparedStatements}"
  11. 11
    p:testOnBorrow="${testOnBorrow}"/>

这里我用了阿里的Druid数据源,虽然Druid数据源可以通过connectProperties和passwordCallback两个参数对属性值进行加密处理,但是项目中用到了spring,以上含有占位符的bean对于spring来说就是一个半成品的bean,还需要用实现BeanFactoryPostProcessor接口的类的postProcessBeanFactory方法进行处理,所以在spring项目中将加密的属性值交给spring处理会比较好。



comments powered by Disqus