一、前言
出于安全考虑,java项目配置文件中不允许出现明文密码;
为了解决这个问题,可以使用jasypt
这个jar包,这个jar包可以对字符串进行加解密,项目中引入后,在配置文件中写加密后的密码即可,项目启动时这个jar包就会对密码进行解密,不影响项目正常使用。
java类中也不允许出现明文密码,也可以利用这个jar包进行加解密。
二、解决方法
1.spring项目中,在pom.xml
里引入:
<!-- https://mvnrepository.com/artifact/org.jasypt/jasypt -->
<dependency>
<groupId>org.jasypt</groupId>
<artifactId>jasypt</artifactId>
<version>1.9.3</version>
</dependency>
2.编写一个加解密工具类ENC_Util.java
,样例如下:
import org.jasypt.encryption.pbe.PooledPBEStringEncryptor;
import org.jasypt.encryption.pbe.StandardPBEStringEncryptor;
import org.jasypt.encryption.pbe.config.EnvironmentStringPBEConfig;
import org.jasypt.encryption.pbe.config.SimpleStringPBEConfig;
public class ENC_Util {
private static final String SALT = "mysalt";
/**
* jasypt-1.9.3 加解密工具类( jasypt-spring-boot-starter 是 2.1.2 )
*/
private static final String PBEWITHMD5ANDDES = "PBEWithMD5AndDES";
private static final String PBEWITHHMACSHA512ANDAES_256 = "PBEWITHHMACSHA512ANDAES_256";
public static String encryptWithMD5(String plainText) {
return encryptWithMD5(plainText,SALT);
}
public static String decryptWithMD5(String plainText) {
//自己的解密方法,解密时,需要把ENC()去掉才行
if(plainText == null || plainText.length()<=5){
return "";
}else{
plainText = plainText.substring(4,plainText.length()-1);
}
return decryptWithMD5(plainText,SALT);
}
/**
* Jasyp2.x 加密(PBEWithMD5AndDES)
* @param plainText 待加密的原文
* @param factor 加密秘钥
* @return java.lang.String
*/
public static String encryptWithMD5(String plainText, String factor) {
// 1. 创建加解密工具实例
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
// 2. 加解密配置
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
config.setAlgorithm(PBEWITHMD5ANDDES);
config.setPassword(factor);
encryptor.setConfig(config);
// 3. 加密
return encryptor.encrypt(plainText);
}
/**
* Jaspy2.x 解密(PBEWithMD5AndDES)
* @param encryptedText 待解密密文
* @param factor 解密秘钥
* @return java.lang.String
*/
public static String decryptWithMD5(String encryptedText, String factor) {
// 1. 创建加解密工具实例
StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor();
// 2. 加解密配置
EnvironmentStringPBEConfig config = new EnvironmentStringPBEConfig();
config.setAlgorithm(PBEWITHMD5ANDDES);
config.setPassword(factor);
encryptor.setConfig(config);
// 3. 解密
return encryptor.decrypt(encryptedText);
}
/**
* Jasyp3.x 加密(PBEWITHHMACSHA512ANDAES_256)
* @param plainText 待加密的原文
* @param factor 加密秘钥
* @return java.lang.String
*/
public static String encryptWithSHA512(String plainText, String factor) {
// 1. 创建加解密工具实例
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
// 2. 加解密配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(factor);
config.setAlgorithm(PBEWITHHMACSHA512ANDAES_256);
// 为减少配置文件的书写,以下都是 Jasyp 3.x 版本,配置文件默认配置
config.setKeyObtentionIterations( "1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
// 3. 加密
return encryptor.encrypt(plainText);
}
/**
* Jaspy3.x 解密(PBEWITHHMACSHA512ANDAES_256)
* @param encryptedText 待解密密文
* @param factor 解密秘钥
* @return java.lang.String
*/
public static String decryptWithSHA512(String encryptedText, String factor) {
// 1. 创建加解密工具实例
PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
// 2. 加解密配置
SimpleStringPBEConfig config = new SimpleStringPBEConfig();
config.setPassword(factor);
config.setAlgorithm(PBEWITHHMACSHA512ANDAES_256);
// 为减少配置文件的书写,以下都是 Jasyp 3.x 版本,配置文件默认配置
config.setKeyObtentionIterations("1000");
config.setPoolSize("1");
config.setProviderName("SunJCE");
config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
config.setIvGeneratorClassName("org.jasypt.iv.RandomIvGenerator");
config.setStringOutputType("base64");
encryptor.setConfig(config);
// 3. 解密
return encryptor.decrypt(encryptedText);
}
public static void main(String[] args) {
String plainText = "3s";
//这个每次跑的结果不一样
String encryptWithMD5Str = encryptWithMD5(plainText, SALT);
//虽然不一样,这个也能正常执行
String decryptWithMD5Str = decryptWithMD5(encryptWithMD5Str, SALT);
System.out.println("加密前:"+plainText);
System.out.println("加密后:"+encryptWithMD5Str);
System.out.println("解密后:"+decryptWithMD5Str);
//String encryptWithSHA512Str = encryptWithSHA512(plainText, factor);
//String decryptWithSHA512Str = decryptWithSHA512(encryptWithSHA512Str, factor);
//System.out.println("采用SHA512加密前原文密文:" + encryptWithSHA512Str);
//System.out.println("采用SHA512解密后密文原文:" + decryptWithSHA512Str);
}
}
可以执行这个类的main方法,把plainText
改成待加密内容,执行后就可以生成加密后的内容;
可以修改SALT
变量,这个就是加解密用的盐值。
3.自定义一个配置文件解析类ENC_PropertyPlaceholderConfigurer
,继承PropertyPlaceholderConfigurer
,其中实现对配置文件加密内容的解密操作。样例如下:
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ENC_PropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
/**
* 将包含ENC()的value进行转换
*/
@Override
protected String convertProperty(String propertyName, String propertyValue) {
//其实是 ^ENC\(.+\)$
//以ENC开头,包含(,中间1-N个字符,结尾是)的,就匹配成功
Pattern pattern = Pattern.compile("^ENC\\(.+\\)$");
Matcher matcher= pattern.matcher(propertyValue);
if (matcher.find()){
//如果是这样的格式,说明是加密后的,就返回解密后的内容
return ENC_Util.decryptWithMD5(propertyValue);
}else{
//否则直接返回即可,不做处理
return propertyValue;
}
}
}
4.把自定义的解析类配置入spring的xml文件中,这样才能让项目启动时使用自己写的这个类。
以本人的项目为例,
项目启动时,一般会先加载web.xml
,其中一般会有:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:META-INF/app_config/context/context-*.xml
</param-value>
</context-param>
这段的意思是会加载resources
文件夹下的META-INF/app_config/context/
里面所有的以context-
开头的xml
文件;
(项目打成war包后会是项目名\WEB-INF\classes\META-INF\app_config\context\
)
因此,以本人的项目为例,就可以在这个路径下的xml文件里增加这样的配置:
<bean name="enc_propertyConfiger" class="com.xxx.config.ENC_PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>META-INF/app_config/properties/db1.properties</value>
<value>META-INF/app_config/properties/db2.properties</value>
</list>
</property>
</bean>
其中,db1.properties和db2.properties里就是数据库连接的账号密码等信息。
5.修改properties
配置文件,把其中明文密码替换为ENC(密文)
格式,样例如下:
其中密文可以执行ENC_Util.java
得到。
jdbc_driverClass=com.mysql.jdbc.Driver
jdbc_url=jdbc:mysql://127.0.0.1:3306/mydb
jdbc_username=root
#jdbc_password=root
#用秘钥mysalt,加密就是这个,解密就是root(注意每次加密后得到的结果都不一样,不过解密得到的结果都一样)
jdbc_password=ENC(l44BhMR+f40JBsP5euOKKA==)
6.如果有的明文密码是配置在xml文件中的,那就可以用EL表达式${}
,让它读取properties中的值,例如:
<bean name="secDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_username}" />
<property name="password" value="${jdbc_password}" />
<property name="driverClassName" value="${jdbc_driverClass}" />
...
注意,因为第4步中配置了自定义配置文件解析类,所以扫描到的properties文件就会加载入项目,xml文件就可以用EL表达式获取到值。
自己要先检查下项目中是否已经配置了默认的配置文件解析类,搜索class=“org.springframework.beans.factory.config.PropertyPlaceholderConfigurer”,如果已存在,那就把这个删掉,换成自定义的(自定义的才能加解密)
7.如果ENC_Util.java里SALT变量也不允许存在,那就可以在启动参数中增加秘钥,后续在xml文件中用${}
使用。(这个还没有试,感觉应该可以)
https://bbs.csdn.net/topics/392314029
通过 jvm 启动参数 参数 -DXXXXX, 这个XXXXX在springxml里面可以直接通过 ${XXXXX} 引用
-Dmy_salt='mysalt'
然后就可以:
<bean name="enc_propertyConfiger" class="com.taikang.udp.timer.common.util.config.ENC_PropertyPlaceholderConfigurer">
<property name="mySALT" value="${my_salt}" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>META-INF/app_config/properties/uat/secondaryDB.properties</value>
</list>
</property>
</bean>
public class ENC_PropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
//这样这个值就注入了,后续可以用
private String mySALT;
public void setMySALT(String mySALT){
this.mySALT = mySALT;
}
三、总结
1.jasypt可以对字符串进行加解密
2.spring项目中,可以把加密后的密码配置在properties文件里
3.然后可以自定义PropertyPlaceholderConfigurer
,在读取properties的时候进行解密
4.xml中,可以用${}
来使用解密后的密码等配置信息
5.java中,可以用jasypt把字符串解密后使用
6.可以在tomcat启动参数中传入全局变量,作为jasypt加解密的秘钥(如果不允许配置在代码里的话),然后在xml中使用${}
获取并注入java中使用