一学就会!Spring Boot + Druid 实现数据库密码优雅加密
本文介绍了数据库密码加密的必要性和实现方案。针对配置文件中明文密码的安全风险,提出了三种解决方案:配置中心管理、环境变量传入和本地加密。重点讲解了Druid加密工具的使用方法,包括密钥生成、加解密实现和YML配置,并指出密钥管理中的常见误区。文章还对比了Druid与Jasypt等工具的差异,最后强调生产环境中应采用更安全的密钥存储方式。通过Druid自带功能,开发者可以低成本实现数据库密码加密,有
第一部分我们为啥要对数据库加密
兄弟们,你们有没有在 application.yml 文件里直接写过数据库密码?是不是每次提交代码到 Git 仓库时,心里都咯噔一下?
就像这样

当然如果数据库在本机不存在泄露问题,但是如果你是部署在服务器上的数据库就会出现以下问题
-
安全风险:一旦代码仓库(如 GitHub)权限泄露,数据库就等于“裸奔”。
-
运维困难:生产环境、测试环境、开发环境密码都不同,每次修改密码都要改代码、重新打包上线,非常繁琐。
-
审计与合规:在很多公司,安全审计是明令禁止配置文件中出现明文密码的。
第二部分:我们如何做
1. 方案概览:
-
配置中心:如 Nacos、Apollo,把密码放在配置中心统一管理(这是大厂主流方案)。
-
环境变量:通过启动脚本传入密码(简单直接,但管理稍乱)。
-
本地加密:在配置文件中使用密文,在程序启动时自动解密。这对于中小型项目来说,是一个成本最低、见效最快的方案。
2. 主角登场:Druid 加密:
-
我们今天的主角就是
druid-spring-boot-starter自带的加密功能。 -
优点:无需引入新的依赖,如果你已经在使用 Druid 连接池,那这个功能就是“白送”的,非常方便。
第三部分:实战演练——手把手带你飞(核心内容,上代码)
-
1. 编写密钥生成和加解密工具:
-
贴出我们的
DruidEncryptUtil.java类的代码。 -
public class DruidEncryptUtil { /** * 私钥 - 用于加密操作 */ public static String privateKey; /** * 公钥 - 用于解密操作 */ public static String publicKey; /** * 静态代码块用于初始化密钥对 * 使用ConfigTools生成512位的RSA密钥对 * 注意:实际生产环境应考虑更安全的密钥存储方式 */ static { try { String[] keyPair = ConfigTools.genKeyPair(512); privateKey = keyPair[0]; System.out.println("privatekey:" + privateKey); publicKey = keyPair[1]; System.out.println("publicKey:" + publicKey); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchProviderException e) { e.printStackTrace(); } } /** * 使用私钥对明文进行加密 * * @param plainText 明文内容 * @return 加密后的字符串 * @throws Exception 加密过程中的异常 */ public static String encrypt(String plainText) throws Exception { String encrypt = ConfigTools.encrypt(privateKey, plainText); System.out.println("encrypt:" + encrypt); return encrypt; } /** * 使用公钥对密文进行解密 * * @param encryptText 加密后的字符串 * @return 解密后的明文 * @throws Exception 解密过程中的异常 */ public static String decrypt(String encryptText) throws Exception { String decrypt = ConfigTools.decrypt(publicKey, encryptText); System.out.println("decrypt:" + decrypt); return decrypt; } /** * 主方法 - 测试加密解密功能 * * @param args 命令行参数(未使用) * @throws Exception 测试过程中可能抛出的异常 */ public static void main(String[] args) throws Exception { String encrypt = encrypt("123456"); System.out.println("encrypt:" + encrypt); decrypt(encrypt); } } -
重点讲解:这个工具类作用就是,生成一对秘钥和公钥,一个加密方法,一个解密方法(用于验证是否可以成功解密),当你运行后输出了123456说明这个工具类没有问题
-
重点讲解与踩坑反思:一个差点“骗”了我的工具类
拿到这个工具类,我的第一反应是它的
static代码块有问题,因为它每次启动都会生成不同的密钥对。我当时心想:“这肯定会导致项目拿着旧公钥解不开新密码,启动必报错!”然而,诡异的事情发生了——项目启动和业务调用一切正常!
经过排查才恍然大悟:这个
DruidEncryptUtil.java只是一个离线的“密钥生成工具”,Spring Boot 应用在运行时根本不会调用它。应用的解密行为,完全依赖于application.yml中那对固定的公钥和密文。但这反而更危险! 如果一个新同事想用这个工具生成新密码,他很可能会忘记更新
yml里的公钥,从而导致生产事故。正确做法:
-
运行一次
main方法,保存好控制台输出的privateKey和publicKey。 -
改造
DruidEncryptUtil.java,删除static代码块,将保存好的密钥作为final常量写死,让它变成一个行为固定的安全工具。 -
decrypt方法在这里的最大作用,就是生成密钥后,当场验证这对密钥是否工作正常。 -
修改后的类
public class DruidEncryptUtil { /** * 私钥 - 用于加密操作 */ public static String privateKey = "MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEAqxmxNwOc9IoS6MVVZ3ZnWpiP2agHcObaeUR+8jVyWMnivn21teh/+QvbPrDeEaY3i5DdarM0egK4i++Rp2HgawIDAQABAkAK1JXnhsg5B0LQwfaSYmfpNisBrgv6iFv5ie2dmBoXpv7xdVK3TmxssBgbyWSvY3SaR9K3/Fqn7eeO+5QFFx3xAiEAzc94Eks5FWGOUwHqJ54cVQAwiwGbkN1hFiy0fwVGJi0CIQDU009VrAcvdC9CPYXWkQ1g2F1fSGTDKlu190PGGHkX9wIgObHjcx1rTzcd8t8iiSClyJ5Y/V7iAWZOBS1bHBCabbECIDDx5+zsAzsGnVe+jmkqMsly+QZQv9uigjT3CL8mIbNBAiBeuqLo+94je9ZXKxPNQeFXI1xtfUhoHmv/XfgXvZjSnA=="; /** * 公钥 - 用于解密操作 */ public static String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKsZsTcDnPSKEujFVWd2Z1qYj9moB3Dm2nlEfvI1cljJ4r59tbXof/kL2z6w3hGmN4uQ3WqzNHoCuIvvkadh4GsCAwEAAQ=="; /** * 使用私钥对明文进行加密 * * @param plainText 明文内容 * @return 加密后的字符串 * @throws Exception 加密过程中的异常 */ public static String encrypt(String plainText) throws Exception { String encrypt = ConfigTools.encrypt(privateKey, plainText); System.out.println("encrypt:" + encrypt); return encrypt; } /** * 使用公钥对密文进行解密 * * @param encryptText 加密后的字符串 * @return 解密后的明文 * @throws Exception 解密过程中的异常 */ public static String decrypt(String encryptText) throws Exception { String decrypt = ConfigTools.decrypt(publicKey, encryptText); System.out.println("decrypt:" + decrypt); return decrypt; } /** * 主方法 - 测试加密解密功能 * * @param args 命令行参数(未使用) * @throws Exception 测试过程中可能抛出的异常 */ public static void main(String[] args) throws Exception { String encrypt = encrypt("123456"); System.out.println("encrypt:" + encrypt); decrypt(encrypt); } }
-
-
2.配置
application.yml:
application.yml 的配置。
server:
port: 3000
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKsZsTcDnPSKEujFVWd2Z1qYj9moB3Dm2nlEfvI1cljJ4r59tbXof/kL2z6w3hGmN4uQ3WqzNHoCuIvvkadh4GsCAwEAAQ==
spring:
main:
allow-circular-references: true
datasource:
username: root
password: JW/fl0/KyOZsUs/iX3vdCtO83ETGuQ3P5gpCG0Z8SU5bU7iCDbwY07X/ySu63Qd75FYU8AeyhYCYPHcoKkn56w==
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/jc-club?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
druid:
type: com.alibaba.druid.pool.DruidDataSource # <--- 请添加这一行,这是最关键的一步
initial-size: 20
min-idle: 20
connection-properties: config.decrypt=true;config.decrypt.key=${publicKey};
max-active: 100
max-wait: 60000
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: 123456
filter:
stat:
enabled: true
slow-sql-millis: 2000
log-slow-sql: true
wall:
enabled: true
config:
enabled: true
logging:
config: classpath:log4j2-spring.xml
逐行解释(解密五人组):
type: com.alibaba.druid.pool.DruidDataSource: 这是入场券。告诉 Spring Boot:“我要用 Druid,请让它上场!”filter.config.enabled: true: 这是执行官。告诉 Druid:“把你的加密解密专家ConfigFilter派出来干活!”connection-properties: ...: 这是操作指令。ConfigFilter拿到剧本,上面写着:“你需要解密,钥匙在……”publicKey: ...: 这是钥匙。ConfigFilter按照指令,拿到了这把公钥。password: ...: 这是上了锁的保险箱。ConfigFilter最终用钥匙打开了它。
第四部分:进阶思考——如何做得更好?(展现深度)
-
1. 密钥的存放:
-
直接把公私钥写在代码里虽然方便,但在严格的生产环境中还不够安全。
-
可以建议读者将密钥存储在环境变量、服务器的外部配置文件或者专业的**密钥管理服务(KMS)**中,程序启动时去读取。
-
-
2. 其他工具 Jasypt:
-
可以简单提一下另一个流行的加密工具
Jasypt。 -
简单对比一下它和 Druid 加密的区别(比如 Jasypt 使用的是对称加密,只需要一个“盐”,配置更简单,但 Druid 的非对称加密理论上更安全)。
-
第五部分:总结
-
我们明白了为什么不能在配置文件中存明文密码。
-
我们学会了如何利用 Druid 自带的功能,零成本地实现密码加密。
-
最后,我们探讨了在生产环境中更安全的密钥管理方式。
更多推荐




所有评论(0)