现在很多项目都是把数据库的密码明文放在配置文件中,这样其实是不安全的,应该将密码加密后再放到配置中,这样一定程度的保护数据库密码的安全,那怎么实现呢,这里提供两种方案:
大体思路:
- 预先根据公钥(自定义)生成加密密码,配置在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
1)依赖包:
2)配置秘钥并生成加密文
注:同一个字符加密多次结果不一样,解密后是一样的。
yaml配置:
jasypt:
encryptor:
password: GOURD-HXN-1314
生成秘钥方式一:
public static void main(String[] args) throws Exception {
// jasypt
BasicTextEncryptor textEncryptor = new BasicTextEncryptor();
textEncryptor.setPassword("GOURD-HXN-1314");
// 加密
String password = textEncryptor.encrypt("gourd123");
System.out.println("^0^===password:"+ password);
// 解密
String originPwd = textEncryptor.decrypt("4M6RKeFuZ7OngpmunjkMm/a+W8eCJrsF");
System.out.println("^0^===originPwd:"+ originPwd);
}
生成秘钥方式二:
在CMD中cd到jar包路径下执行语句(input:明文信息,password:秘钥):
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密码、邮件密码都可以用此方式加密