导读

Spring-使用外部属性文件01

Spring-使用加密的属性文件02

Spring-属性文件自身的引用03


概述

对于不敏感的属性信息,以明文形式出现在属性文件中是合适的,但是如果属性信息是数据库用户名和密码等敏感信息,一般希望以密文的方式保存。

这就要求对应用程序配置文件的某些属性进行加密,让Spring容器在读取属性文件后,在内存中对属性进行解密,然后将解密后的属性值赋给目标对象。

我们来看下 PropertyPlaceholderConfigurer的继承关系

Spring-使用加密的属性文件02_Spring学习

PropertyResourceConfigurer类中有几个方法

Spring-使用加密的属性文件02_Spring教程_02


实例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster

Spring-使用加密的属性文件02_Spring学习_03

DES加密解密工具类

信息的加密分为对称和非对称两种方式, 前者表示加密后的信息可以解密为原值,而后者则不能根据加密后的信息还原为原值。

MD5属于非对称加密, DES属于对称加密。

先用DES对属性值进行加密,在读取到属性值时,在用DES进行解密。

DES加密解密工具类

package com.xgj.ioc.propertyplacehoderEncryption;

import java.security.Key;
import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class DESUtils {
    private static Key key;
    // 指定DES加密解密用的密钥
    private static String KEY_STR = "myKey";
    static {
        try {
            KeyGenerator generator = KeyGenerator.getInstance("DES");
            generator.init(new SecureRandom(KEY_STR.getBytes()));
            key = generator.generateKey();
            generator = null;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 
     * 
     * @Title: getEncryptString
     * 
     * @Description: 对字符串进行加密,返回BASE64编码的加密字符串
     * 
     * @param str
     * @return
     * 
     * @return: String
     */
    public static String getEncryptString(String str) {
        BASE64Encoder base64en = new BASE64Encoder();
        try {
            byte[] strBytes = str.getBytes("UTF8");
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] encryptStrBytes = cipher.doFinal(strBytes);
            return base64en.encode(encryptStrBytes);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 
     * 
     * @Title: getDecryptString
     * 
     * @Description:对BASE64编码的加密字符串进行解密,返回解密后的字符串
     * 
     * @param str
     * @return
     * 
     * @return: String
     */
    public static String getDecryptString(String str) {
        BASE64Decoder base64De = new BASE64Decoder();
        try {
            byte[] strBytes = base64De.decodeBuffer(str);
            Cipher cipher = Cipher.getInstance("DES");
            cipher.init(Cipher.DECRYPT_MODE, key);
            byte[] decryptStrBytes = cipher.doFinal(strBytes);
            return new String(decryptStrBytes, "UTF8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 
     * 
     * @Title: main
     * 
     * @Description: 测试方法
     * 
     * @param args
     * @throws Exception
     * 
     * @return: void
     */
    public static void main(String[] args) throws Exception {
        System.out.println(getEncryptString("cc"));
        System.out.println(getEncryptString("zsmart2017"));

        System.out.println(getDecryptString("SkR6wWI9iws="));
        System.out.println(getDecryptString("lSR/mscM1NE3sM98QFjAdw=="));
    }
}

使用密文版的属性文件

  1. 运行 DESUtils, 得到 用户名和密码的加密字符串
  2. 修改jdbc.properties
jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@172.25.246.11:1521:xgj
jdbc.username=SkR6wWI9iws=
jdbc.password=lSR/mscM1NE3sM98QFjAdw==

PropertyPlaceholderConfigurer 本身不支持密文版的属性文件,不过我们可以扩展该类,重写 String convertProperty(String propertyName, String propertyValue)方法

package com.xgj.ioc.propertyplacehoderEncryption;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

/**
 * 
 * 
 * @ClassName: EncryptPropertyPlaceholderConfigurer
 * 
 * @Description: 继承PropertyPlaceholderConfigurer,重写convertProperty方法,对属性进行解密
 * 
 * @author: Mr.Yang
 * 
 * @date: 2017年8月6日 下午11:01:23
 */
public class EncryptPropertyPlaceholderConfigurer extends
        PropertyPlaceholderConfigurer {
    // 对应jdbc.properties中的key
    private String[] encryptPropNames = { "jdbc.username", "jdbc.password" };

    @Override
    protected String convertProperty(String propertyName, String propertyValue) {
        if (isEncryptProp(propertyName)) {
            String decryptValue = DESUtils.getDecryptString(propertyValue);
            System.out.println("解密后的字符串:" + decryptValue);
            return decryptValue;
        } else {
            return propertyValue;
        }
    }

    /**
     * 判断是否是加密的属性
     * 
     * @param propertyName
     * @return
     */
    private boolean isEncryptProp(String propertyName) {
        for (String encryptPropName : encryptPropNames) {
            if (encryptPropName.equals(propertyName)) {
                return true;
            }
        }
        return false;
    }
}

EncryptPropertyPlaceholderConfigurer使用DESUtils中的方法解密加密后的字符串。


修改配置文件,引用自定义的EncryptPropertyPlaceholderConfigurer

<!-- 引入JDBC属性文件  加密-->
    <bean  class="com.xgj.ioc.propertyplacehoderEncryption.EncryptPropertyPlaceholderConfigurer"
        p:location="classpath:spring/jdbc.properties"
        p:fileEncoding="utf-8"/> 

使用自定义的属性加载器后,就无法使用context:property-placeholder属性加载配置文件了,必须使用传统的方式引用加密版的属性文件,如上

完整的配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
    <context:component-scan base-package="com.xgj.ioc.propertyplacehoderEncryption"/>

    <!-- 引入JDBC属性文件  加密-->
    <bean  class="com.xgj.ioc.propertyplacehoderEncryption.EncryptPropertyPlaceholderConfigurer"
        p:location="classpath:spring/jdbc.properties"
        p:fileEncoding="utf-8"/> 

    <!-- 通过属性名引用属性值 -->     
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.url}"
        p:username="${jdbc.username}"
        p:password="${jdbc.password}"/>

    <!-- 配置Jdbc模板  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />

</beans>

测试类

package com.xgj.ioc.propertyplacehoderEncryption;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;

@Component
public class PropertyPlaceHoderEncryptionTest {

    private final static String MATCH_COUNT_SQL = " SELECT count(*) FROM temp_user  "
            + " WHERE user_name =? and password=? ";

    private JdbcTemplate jdbcTemplate;

    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 
     * 
     * @Title: getMatchCount
     * 
     * @Description: 根据用户名和密码判断用户是否存在
     * 
     * @param username
     * @param password
     * 
     * @return: int
     */
    public int getMatchCount(String username, String password) {
        return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[] {
                username, password }, Integer.class);
    }

    /**
     * 
     * 
     * @Title: main
     * 
     * @Description: 测试
     * 
     * @param args
     * 
     * @return: void
     */
    public static void main(String[] args) {
        // 加载Spring配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/propertyplacehoderEncryption/beans.xml");
        // 获取通过注解标注的Bean
        PropertyPlaceHoderEncryptionTest propertyplacehoderEncryption = ctx
                .getBean("propertyPlaceHoderEncryptionTest",
                        PropertyPlaceHoderEncryptionTest.class);
        // 调用方法
        int count = propertyplacehoderEncryption.getMatchCount("xgj", "123456");
        System.out.println("匹配的用户数量:" + count);
    }
}

测试结果

Spring-使用加密的属性文件02_Spring学习_04