springboot+druid 数据库的密码加密的两种方式

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/HXNLYW/article/details/98635913

现在很多项目都是把数据库的密码明文放在配置文件中,这样其实是不安全的,应该将密码加密后再放到配置中,这样一定程度的保护数据库密码的安全,那怎么实现呢,这里提供两种方案:

一、实现密码回调逻辑。

大体思路:

  • 预先根据公钥(自定义)生成加密密码,配置在yml文件中
  • 实现加解密算法。
  • 编写自己的回调类,实现自己的回调逻辑,并配置到yml中

1)、配置文件(公钥、密码接口回调类配置)

spring:
  datasource:
    # 公钥
    publicKey: GOURD-HXNLYW-201314
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      url: jdbc:mysql://47.103.5.190:3306/gourd?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: hWm9HtDn605aupyXTuuA5Q==
      # 配置 connection-properties,启用加密,配置公钥。
      connection-properties: config.decrypt=true;publicKey=${spring.datasource.publicKey};password=${spring.datasource.master.password}
      passwordCallbackClassName: com.gourd.index.config.DbPasswordCallback
    slave:
      url: jdbc:mysql://47.103.5.190:3306/gourd?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: hWm9HtDn605aupyXTuuA5Q==
      # 配置 connection-properties,启用加密,配置公钥。
      connection-properties: config.decrypt=true;publicKey=${spring.datasource.publicKey};password=${spring.datasource.slave.password}
      passwordCallbackClassName: com.gourd.index.config.DbPasswordCallback

    druid:
      initial-size: 5
      max-wait: 60000
      min-idle: 1
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      validation-query: select 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 50
      max-pool-prepared-statement-per-connection-size: 20

2)、加解密工具类(自定义):

import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;

import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

/**
 * AesHope工具类
 *
 * @author: gourd
 *
 **/
@Slf4j
public class AesHopeUtil {
    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";

    /***
     * AES加密
     * @param password
     * @param content
     * @return
     * @throws Exception
     */
    public static String encrypt(String password, String content){
        //创建密码器
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            byte[] bytes = content.getBytes("utf-8");
            //初始化密码器
            cipher.init(Cipher.ENCRYPT_MODE, getSecretKeySpec(password));
            //加密
            byte[] bytes1 = cipher.doFinal(bytes);
            //通过BASE64转码返回
            return Base64.encodeBase64String(bytes1);
        } catch (NoSuchAlgorithmException e) {
            log.error("{}",e);
        } catch (NoSuchPaddingException e) {
            log.error("{}",e);
        } catch (BadPaddingException e) {
            log.error("{}",e);
        } catch (UnsupportedEncodingException e) {
            log.error("{}",e);
        } catch (IllegalBlockSizeException e) {
            log.error("{}",e);
        } catch (InvalidKeyException e) {
            log.error("{}",e);
        }
        return null;
    }

    /***
     * AES解密
     * @param password
     * @param content
     * @return
     * @throws Exception
     */
    public static String decryt(String password, String content) {
        Cipher cipher = null;
        try {
            cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            //使用密钥初始化解密
            cipher.init(Cipher.DECRYPT_MODE, getSecretKeySpec(password));
            byte[] bytes = cipher.doFinal(Base64.decodeBase64(content));
            return new String(bytes, "utf-8");
        } catch (NoSuchAlgorithmException e) {
            log.error("{}",e);
        } catch (NoSuchPaddingException e) {
            log.error("{}",e);
        } catch (BadPaddingException e) {
            log.error("{}",e);
        } catch (UnsupportedEncodingException e) {
            log.error("{}",e);
        } catch (IllegalBlockSizeException e) {
            log.error("{}",e);
        } catch (InvalidKeyException e) {
            log.error("{}",e);
        }
        return null;
    }

    /***
     * 生成加密密钥
     * @param password
     * @return
     * @throws NoSuchAlgorithmException
     */
    private static SecretKeySpec getSecretKeySpec(final String password)  {
        //返回密钥生成器对象
        KeyGenerator keyGenerator = null;
        try {
            keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(password.getBytes());
            //设置AES密钥长度
            keyGenerator.init(128, secureRandom);
            //生成一个密钥
            SecretKey secretKey = keyGenerator.generateKey();
            //转换为AES密钥
            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            log.error("{}",e);
        }
        return null;
    }
}

3)、回调类,实现自己的回调逻辑

import com.alibaba.druid.util.DruidPasswordCallback;
import com.gourd.common.utils.AesHopeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Properties;

/**
 * 数据库回调密码解密
 * 
 * @author gourd
 *
 */
@Component
@Slf4j
public class DbPasswordCallback extends DruidPasswordCallback {

	@Override
	public void setProperties(Properties properties) {
		super.setProperties(properties);
		String password = properties.getProperty("password");
		String publicKey = properties.getProperty("publicKey");
		if (StringUtils.isNotEmpty(password)) {
			try {
				//所以这里的代码是将密码进行解密
				String sourcePassword = AesHopeUtil.decryt(publicKey, password);
				setPassword(sourcePassword.toCharArray());
			} catch (Exception e) {
				setPassword(password.toCharArray());
			}
		}
	}

	/**
	 * 生成加密后的密码,放到yml中
	 * @param args
	 */
	public static void main(String[] args) {
		// 生成加密后的密码,放到yml中
		String password = "gourd123";
		String pwd = AesHopeUtil.encrypt("GOURD-HXNLYW-201314",password);
		System.out.println(pwd);

		String source = AesHopeUtil.decryt("GOURD-HXNLYW-201314",pwd);
		System.out.println(source);
	}
}

Druid多数据源配置请移步:https://blog.csdn.net/HXNLYW/article/details/90519757

二、借助第三方工具(jasypt

1)依赖包:

<!--文本加解密-->
<dependency>
    <groupId>com.github.ulisesbocchio</groupId>
    <artifactId>jasypt-spring-boot-starter</artifactId>
    <version>1.16</version>
</dependency>

2)配置秘钥并生成加密文

yaml配置:

jasypt:
  encryptor:
    password: GOURD-HXN-1314

生成秘钥:

在CMD中cd到jar包路径下执行语句:

java -cp jasypt-1.9.2.jar org.jasypt.intf.cli.JasyptPBEStringEncryptionCLI input="hxnlyw123" password=GOURD-HXN-1314 algorithm=PBEWithMD5AndDES

操作截图:

3)将OUTPUT内容替换数据库明文密码并加解密函数:

jasypt:
  encryptor:
    password: GOURD-HXN-1314

spring:
  datasource:
    # 公钥
    type: com.alibaba.druid.pool.DruidDataSource
    master:
      url: jdbc:mysql://47.103.5.190:3306/gourd?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: ENC(4M6RKeFuZ7OngpmunjkMm/a+W8eCJrsF)
    slave:
      url: jdbc:mysql://47.103.5.190:3306/gourd?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
      driver-class-name: com.mysql.cj.jdbc.Driver
      username: root
      password: ENC(4M6RKeFuZ7OngpmunjkMm/a+W8eCJrsF)

 到此配置完成。redis密码、邮件密码都可以用此方式加密

猜你喜欢

转载自blog.csdn.net/HXNLYW/article/details/98635913