一、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
<bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
<property name="customPropertyFiles">
<list>
<value>[filepath]test.properties</value>
</list>
</property>
</bean>
5.2 CustomPropertyPlaceConfigurer.java
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
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
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);
}
}