spring项目配置文件不允许出现明文密码的解决方法(jasypt使用方法)

一、前言

出于安全考虑,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中使用

猜你喜欢

转载自blog.csdn.net/BHSZZY/article/details/130251647