spring扩展:PropertyPlaceHolderConfigurer


一 PropertyPlaceHolderConfigurer介绍
   主要用于将一些配置信息移出xml文件,至properties文件。

二 常规使用

三 拓展使用
1、将porperties内容设置成java全局可读
    思路,spring启动的时候会将properties加载至其指定缓存机制,可利用其加载机制,在读取properties时,保存一份至变量,对外提供访问接口。

    1.1 创建PropertyPlaceHolderConfigurer的子类,代码如下CustomPropertyPlaceConfigurer
        private Map<String, String> ctxPropertiesMap;
        
        @Override
        protected void processProperties(
                ConfigurableListableBeanFactory beanFactoryToProcess,
                Properties props) throws BeansException {
            
            super.processProperties(beanFactoryToProcess, props);
            
            ctxPropertiesMap = new HashMap<String, String>();
            for(Object key: props.keySet()) {
                String keyStr = key.toString();
                String valueStr = props.getProperty(keyStr);
                ctxPropertiesMap.put(keyStr, valueStr);
            }
        }
        
        public String getContextProperty(String key) {
            return ctxPropertiesMap.get(key);
        }
    
        重写processProperties方法,将properties内容存放至ctxPropertiesMap中。

    1.2 spring相关配置
        <bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
           <property name="locations">
         <list>
        <value>classpath:test.properties</value>
         </list>
       </property>
    </bean>

      其中test.properties内容
      username=root
      password=123456789

2. 将properties文件的路径设置成运行时拼接
    起因:公司要将tomcat jdk mysql等都做到一个安装包,实现一键安装,但又要求相关配置文件(如数据库连接的properties文件)不能放在项目中,
    以免项目升级时容易将开发环境配置覆盖生产环境配置。而配置文件必须在安装包选择的安装目录中。描述这么多,就为了说明,配置文件是动态可变的,项目启动时才能知道。

    2.1 修改spring相关配置,将默认locations属性修改为自定义属性,将资源文件路径改成用变量替代
        <bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
        <property name="customPropertyFiles">
            <list>
                <value>[filepath]test.properties</value>
            </list>
        </property>
        </bean>
        其中,customPropertyFiles为新增属性,通过setter注入,[filepath]是动态值,在加载时替换

    2.2 在类CustomPropertyPlaceConfigurer中,添加customPropertyFiles的setter注入方法。
    //根据环境变量读取资源文件(实现动态读取资源文件)
    public void setCustomPropertyFiles(List<String> customPropertyFiles) {
        String fileSeparator = System.getProperty("file.separator");
        String javaHome = System.getenv("JAVA_HOME");
        Properties properties = new Properties();
        
        for(String customPropertyFile: customPropertyFiles) {
            customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
            
            Properties pro = PropertyUtils.getProperties(customPropertyFile);
            properties.putAll(pro);
        }
        
        //关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring
        this.setProperties(properties);
    }

    读取环境变量JAVA_HOME,替换[filepath],即需要将test.properties文件存放至JAVA_HOME下(如果一键安装包,将选择的安装路径添加至环境变量,则就可以攻击环境变量找到
    相关资源文件了)。

3。设置数据库密码成加密状态

    起因:生产环境数据库的密码需要对外保密,知道的人越少越安全,故在配置中,将数据库密码加密可以防止被开发人员获得。

    3.1 将加密的密码存放至test.properties中
    username=root
    password=4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C

    3.2 步骤1已经得到资源文件,在此解密,并存入资源文件
    @Override
    protected void processProperties(
            ConfigurableListableBeanFactory beanFactoryToProcess,
            Properties props) throws BeansException {
        String password = props.getProperty("password");
        password = decryptByPrivateKey(password);
        props.put("password", password);
        
        super.processProperties(beanFactoryToProcess, props);
        
        ctxPropertiesMap = new HashMap<String, String>();
        for(Object key: props.keySet()) {
            String keyStr = key.toString();
            String valueStr = props.getProperty(keyStr);
            ctxPropertiesMap.put(keyStr, valueStr);
        }
    }

4。实现配置文件不同场景下切换
    针对步骤2中的起因,如果不将配置文件移出项目,可以用两个配置文件,或者一个配置文件中配置两套配置来解决。
    此处用两个配置文件,这样,开发阶段,开发人员将不会再操作生产配置文件
    4.1 在步骤2基础上,在JAVA_HOME目录下添加资源文件test_dev.properties
    username=root_dev
     password=4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C

    4.2 添加环境变量devMode
    通过此变量实现生产环境和开发环境的切换,在生产环境不需配置此变量

    4.3 修改实现类
    private static final String propertiesName = "test";
    
    //根据环境变量读取资源文件(实现动态读取资源文件)
    public void setCustomPropertyFiles(List<String> customPropertyFiles) {
        String fileSeparator = System.getProperty("file.separator");
        String javaHome = System.getenv("JAVA_HOME");
        String devMode = System.getenv("devMode");
        Properties properties = new Properties();
        
        for(String customPropertyFile: customPropertyFiles) {
            customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
            
            if(customPropertyFile.indexOf(propertiesName) > -1) {
                String selectMode = devMode == null ? "":devMode;
                customPropertyFile = customPropertyFile.replace(propertiesName, propertiesName + selectMode);
            }
            Properties pro = PropertyUtils.getProperties(customPropertyFile);
            properties.putAll(pro);
        }
        
        //关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring
        this.setProperties(properties);
    }

5。相关代码

    5.1 applicationContext-propertyHolderPlaceConfigurer.xml

          

<?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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
	
	<bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
		<property name="customPropertyFiles">
			<list>
				<value>[filepath]test.properties</value>
			</list>
		</property>
	</bean>
	
</beans>

    5.2 CustomPropertyPlaceConfigurer.java

package com.baggio.common.spring.ext;

import java.security.interfaces.RSAPrivateKey;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.baggio.common.utils.PropertyUtils;
import com.baggio.common.utils.RSAUtils;

public class CustomPropertyPlaceConfigurer extends
		PropertyPlaceholderConfigurer {
	
	private Map<String, String> ctxPropertiesMap;
	
	private static final String modulus = "92441782767737168436954696909693024093858863793961614402344444228605172372365323551758683343929072119833664753887689264694957015832845985460688129219184238713260973364694630820191644812136716548792835729282422798366717212011721279467766827116978941847031775579004384556944199142453248284103715410910396365107";
	
	private static final String privateKey = "32679115967513537671046377130533408337374645717358174504074279931768689954116908251020723800481388267273545402109800340028575676988805953753654005794437645857893594581228615872260459962755278225682193031026820777624756496861897491016205372128604377845082143258059385192300542462832051528405770845154897431553";
	
	//4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C
	private static final String propertiesName = "test";
	
	//根据环境变量读取资源文件(实现动态读取资源文件)
	public void setCustomPropertyFiles(List<String> customPropertyFiles) {
		String fileSeparator = System.getProperty("file.separator");
		String javaHome = System.getenv("JAVA_HOME");
		String devMode = System.getenv("devMode");
		Properties properties = new Properties();
		
		for(String customPropertyFile: customPropertyFiles) {
			customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
			
			if(customPropertyFile.indexOf(propertiesName) > -1) {
				String selectMode = devMode == null ? "":devMode;
				customPropertyFile = customPropertyFile.replace(propertiesName, propertiesName + selectMode);
			}
			Properties pro = PropertyUtils.getProperties(customPropertyFile);
			properties.putAll(pro);
		}
		
		//关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring
		this.setProperties(properties);
	}
	
	@Override
	protected void processProperties(
			ConfigurableListableBeanFactory beanFactoryToProcess,
			Properties props) throws BeansException {
		String password = props.getProperty("password");
		password = decryptByPrivateKey(password);
		props.put("password", password);
		
		super.processProperties(beanFactoryToProcess, props);
		
		ctxPropertiesMap = new HashMap<String, String>();
		for(Object key: props.keySet()) {
			String keyStr = key.toString();
			String valueStr = props.getProperty(keyStr);
			ctxPropertiesMap.put(keyStr, valueStr);
		}
	}
	
	public Map<String, String> getCtxPropertiesMap() {
		return ctxPropertiesMap;
	}
	
	public String getContextProperty(String key) {
		return ctxPropertiesMap.get(key);
	}
	
	private String decryptByPrivateKey(String password){
		String mingwen = null;
		try {
			RSAPrivateKey priKey = RSAUtils
					.getPrivateKey(modulus, privateKey);
			// 解密后的明文
			mingwen = RSAUtils.decryptByPrivateKey(password, priKey);
		} catch(Exception e) {
			e.printStackTrace();
		}
		return mingwen;
	}
	
	public static void main(String[] args) {
		ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext-propertyHolderPlaceConfigurer.xml");
		CustomPropertyPlaceConfigurer cu = (CustomPropertyPlaceConfigurer) context.getBean("customPropertyPlaceConfigurer");
		
		Map<String, String> map = cu.getCtxPropertiesMap();
		Iterator<String> it = map.keySet().iterator();
		while(it.hasNext()) {
			String key = (String)it.next();
			String value = map.get(key);
			System.out.println(key + ":" + value);
		}
		
	}
}


    5.3 PropertyUtils.java

package com.baggio.common.utils;

import java.io.FileInputStream;
import java.util.Properties;

public class PropertyUtils {
	public static Properties getProperties(String filename) {
		FileInputStream fis = null;
		Properties p = new Properties();
		
		try {
			fis = new FileInputStream(filename);
			p.load(fis);
		}catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(fis != null) {
				try {
					fis.close();
				}catch (Exception e) {}
			}
		}
		return p;
	}
}


    5.4 RSAUtils.java

package com.baggio.common.utils;

import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.util.HashMap;

import javax.crypto.Cipher;

public class RSAUtils {

	/**
	 * 生成公钥和私钥
	 * 
	 * @throws NoSuchAlgorithmException
	 * 
	 */
	public static HashMap<String, Object> getKeys()
			throws NoSuchAlgorithmException {
		HashMap<String, Object> map = new HashMap<String, Object>();
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		keyPairGen.initialize(1024);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		map.put("public", publicKey);
		map.put("private", privateKey);
		return map;
	}

	/**
	 * 使用模和指数生成RSA公钥
	 * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
	 * /None/NoPadding】
	 * 
	 * @param modulus
	 *            模
	 * @param exponent
	 *            指数
	 * @return
	 */
	public static RSAPublicKey getPublicKey(String modulus, String exponent) {
		try {
			BigInteger b1 = new BigInteger(modulus);
			BigInteger b2 = new BigInteger(exponent);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
			return (RSAPublicKey) keyFactory.generatePublic(keySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 使用模和指数生成RSA私钥
	 * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
	 * /None/NoPadding】
	 * 
	 * @param modulus
	 *            模
	 * @param exponent
	 *            指数
	 * @return
	 */
	public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
		try {
			BigInteger b1 = new BigInteger(modulus);
			BigInteger b2 = new BigInteger(exponent);
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
			return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 公钥加密
	 * 
	 * @param data
	 * @param publicKey
	 * @return
	 * @throws Exception
	 */
	public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
			throws Exception {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.ENCRYPT_MODE, publicKey);
		// 模长
		int key_len = publicKey.getModulus().bitLength() / 8;
		// 加密数据长度 <= 模长-11
		String[] datas = splitString(data, key_len - 11);
		String mi = "";
		// 如果明文长度大于模长-11则要分组加密
		for (String s : datas) {
			mi += bcd2Str(cipher.doFinal(s.getBytes()));
		}
		return mi;
	}

	/**
	 * 私钥解密
	 * 
	 * @param data
	 * @param privateKey
	 * @return
	 * @throws Exception
	 */
	public static String decryptByPrivateKey(String data,
			RSAPrivateKey privateKey) throws Exception {
		Cipher cipher = Cipher.getInstance("RSA");
		cipher.init(Cipher.DECRYPT_MODE, privateKey);
		// 模长
		int key_len = privateKey.getModulus().bitLength() / 8;
		byte[] bytes = data.getBytes();
		byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
		//System.err.println(bcd.length);
		// 如果密文长度大于模长则要分组解密
		String ming = "";
		byte[][] arrays = splitArray(bcd, key_len);
		for (byte[] arr : arrays) {
			ming += new String(cipher.doFinal(arr));
		}
		return ming;
	}

	/**
	 * ASCII码转BCD码
	 * 
	 */
	public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
		byte[] bcd = new byte[asc_len / 2];
		int j = 0;
		for (int i = 0; i < (asc_len + 1) / 2; i++) {
			bcd[i] = asc_to_bcd(ascii[j++]);
			bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
		}
		return bcd;
	}

	public static byte asc_to_bcd(byte asc) {
		byte bcd;

		if ((asc >= '0') && (asc <= '9'))
			bcd = (byte) (asc - '0');
		else if ((asc >= 'A') && (asc <= 'F'))
			bcd = (byte) (asc - 'A' + 10);
		else if ((asc >= 'a') && (asc <= 'f'))
			bcd = (byte) (asc - 'a' + 10);
		else
			bcd = (byte) (asc - 48);
		return bcd;
	}

	/**
	 * BCD转字符串
	 */
	public static String bcd2Str(byte[] bytes) {
		char temp[] = new char[bytes.length * 2], val;

		for (int i = 0; i < bytes.length; i++) {
			val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
			temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

			val = (char) (bytes[i] & 0x0f);
			temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
		}
		return new String(temp);
	}

	/**
	 * 拆分字符串
	 */
	public static String[] splitString(String string, int len) {
		int x = string.length() / len;
		int y = string.length() % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		String[] strings = new String[x + z];
		String str = "";
		for (int i = 0; i < x + z; i++) {
			if (i == x + z - 1 && y != 0) {
				str = string.substring(i * len, i * len + y);
			} else {
				str = string.substring(i * len, i * len + len);
			}
			strings[i] = str;
		}
		return strings;
	}

	/**
	 * 拆分数组
	 */
	public static byte[][] splitArray(byte[] data, int len) {
		int x = data.length / len;
		int y = data.length % len;
		int z = 0;
		if (y != 0) {
			z = 1;
		}
		byte[][] arrays = new byte[x + z][];
		byte[] arr;
		for (int i = 0; i < x + z; i++) {
			arr = new byte[len];
			if (i == x + z - 1 && y != 0) {
				System.arraycopy(data, i * len, arr, 0, y);
			} else {
				System.arraycopy(data, i * len, arr, 0, len);
			}
			arrays[i] = arr;
		}
		return arrays;
	}

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		HashMap<String, Object> map = RSAUtils.getKeys();
		// 生成公钥和私钥
		RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
		RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private");

		// 模
		String modulus = publicKey.getModulus().toString();
		// 公钥指数
		String public_exponent = publicKey.getPublicExponent().toString();
		// 私钥指数
		String private_exponent = privateKey.getPrivateExponent().toString();
		// 明文
		String ming = "123456789";
		// 使用模和指数生成公钥和私钥
		RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent);
		System.out.println("模:" + modulus);
		System.out.println("私钥:" + private_exponent);
		RSAPrivateKey priKey = RSAUtils
				.getPrivateKey(modulus, private_exponent);
		// 加密后的密文
		String mi = RSAUtils.encryptByPublicKey(ming, pubKey);
		System.out.println("加密后:"  + mi);
		// 解密后的明文
		ming = RSAUtils.decryptByPrivateKey(mi, priKey);
		System.out.println("解密后:" + ming);
	}
}


6。参考

http://blog.csdn.net/zhongweijian/article/details/8955825

http://blog.csdn.net/zhongweijian/article/details/8955825

http://blog.csdn.net/collection4u/article/details/8499217




   
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值