跳到主要内容
版本:4.6.0

数据库密码加密

基础介绍​

用 Druid 时可以直接用它自带的 ConfigTools 加密。但如果项目用的是 HikariCP、BeeCP 或者干脆不用连接池,又想加密数据库信息呢?

本框架内置了一套独立的加解密能力,不依赖任何连接池,支持对 url、username、password(以及任何需要脱敏的配置项)做 ENC(...) 包裹。

使用的算法是 RSA,密钥方向与「教科书 RSA」相反:

私钥加密,公钥解密。 公钥可以放心发布出去(只用于运行时解密),私钥只在生成密文的那台机器上使用。

这个方向是刻意为之——配置里只需要公钥(public-key),私钥永远不进配置文件,普通开发者接触不到,泄露面更小。

使用步骤​

1 · 生成密钥对与密文​

import com.baomidou.dynamic.datasource.toolkit.CryptoUtils;

public class Demo {

public static void main(String[] args) throws Exception {
// 生成密钥对:arr[0] = 私钥,arr[1] = 公钥
String[] arr = CryptoUtils.genKeyPair(512);

System.out.println("privateKey: " + arr[0]); // 只留在生成端,不要进配置
System.out.println("publicKey: " + arr[1]); // 填到 public-key

// 用【私钥】加密
System.out.println("url: " + CryptoUtils.encrypt(arr[0], "jdbc:mysql://127.0.0.1:3306/order"));
System.out.println("username: " + CryptoUtils.encrypt(arr[0], "root"));
System.out.println("password: " + CryptoUtils.encrypt(arr[0], "123456"));
}
}
必须使用自定义密钥对

CryptoUtils 内置了一组默认密钥对,且它写在开源代码里——用默认公钥加密等于没有保密性,任何拿到本框架的人都能解出你的密码。它只能起到「防止明文被一眼看到」的作用。

生产环境务必用 genKeyPair 生成自己的密钥对,把公钥配到 public-key,并且不要提交私钥。

keySize 必须是 512

CryptoUtils 内部的分段加解密阈值是写死的 MAX_ENCRYPT_BLOCK = 53 / MAX_DECRYPT_BLOCK = 64(对应 512 位密钥)。

传入更大的位数(如 1024、2048)能生成密钥对、也能加密,但解密时按 64 字节切分单个 RSA 密文块会直接报错。要保持解密可用,genKeyPair 的参数只能用 512。

超长内容的处理:单段最多 53 字节,超过会自动分段加密,所以很长的 JDBC URL 也能正常加密,无需自己切分。

2 · 在 YAML 中配置​

ENC(xxx) 中包裹的 xxx 就是上一步生成的字符串。

spring:
datasource:
dynamic:
public-key: MFwwDQYJKoZIhvcNAQEB... # 上一步生成的公钥
datasource:
master:
url: ENC(xxxxx)
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.cj.jdbc.Driver
slave_1:
url: ENC(xxxxx)
username: ENC(xxxxx)
password: ENC(xxxxx)
driver-class-name: com.mysql.cj.jdbc.Driver
public-key: MFwwDQYJKoZI... # 每个数据源可独立设置,不写则继承全局

解析优先级:单数据源的 public-key > 全局 public-key > 内置默认值。

自定义解密​

如果公司要求用自己的加解密方案(比如接 KMS、国密算法),实现 DataSourceInitEvent 并注册为 Bean 即可。从 3.5.0 开始支持。

public interface DataSourceInitEvent {

/**
* 连接池创建前执行(可用于参数解密)
*
* @param dataSourceProperty 数据源基础信息
*/
void beforeCreate(DataSourceProperty dataSourceProperty);

/**
* 连接池创建后执行
*
* @param dataSource 连接池
*/
void afterCreate(DataSource dataSource);
}
@Bean
public DataSourceInitEvent myInitEvent() {
return new DataSourceInitEvent() {
@Override
public void beforeCreate(DataSourceProperty property) {
property.setPassword(kmsDecrypt(property.getPassword()));
property.setUrl(kmsDecrypt(property.getUrl()));
property.setUsername(kmsDecrypt(property.getUsername()));
}

@Override
public void afterCreate(DataSource dataSource) {
// 可选:连接池创建后的钩子
}
};
}

框架默认的实现是 EncDataSourceInitEvent,也就是 ENC(...) 这一套。自定义实现会替换默认行为,两者不要混用。

常见报错与坑​

报错 / 问题原因与解决
Data must not be longer than 53 bytes(#367)单段上限 53 字节,用第三方工具加密必然踩到。请用 CryptoUtils.encrypt(...),它的分段逻辑与解密侧严格配套
could not decrypt an ENC(...) property, check the public-key configuration(4.6+)fail-fast 设计:公钥配错、长度不匹配或密文被截断。4.6 之前会把密文当明文直接拿去连库,表现为莫名的 Access denied——老版本建议升级以拿到准确报错(#490)
单库配了不同 public-key 却不生效(#598、#623)单数据源 public-key 优先于全局;确认它配在该数据源节点下,且密钥文本完整(Base64,无换行)
url: ENC(...) 解密失败(#720)url 同样走解密,确认密文里没有多余空格/换行
与 jasypt 冲突(#168、#672)jasypt 默认语法也是 ENC(...),两者会互相抢着解密。改掉 jasypt 的前后缀(jasypt.encryptor.property.prefix / suffix)避免撞车,或两者只用其一
默认内置密钥是公开的

框架内置的默认密钥对写在开源代码里,只能防肩窥/误传,不防有心人。生产请用上面的 genKeyPair 生成自己的密钥对,或接 KMS;public-key 是「解密密钥」,本身不敏感,私钥只用于离线加密,不要提交进仓库。

为什么不是公钥加密、私钥解密?​

这是作者参考 Druid 的实现方式保留的设计。Druid 作者 wenshao 在 issue #960 中解释过取舍:配置侧只需要持有公钥,私钥可以被控制在少数人手里,从而限制「谁能生成密文」。

如果你所在的环境对密钥方向有硬性要求,用上面的 DataSourceInitEvent 替换成自己的实现即可。

相关阅读​