阿里巴巴Druid,轻松实现MySQL数据库加密!

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

为什么要加密?

现在的开发习惯,无论是公司的项目还是个人的项目,都会选择将源码上传到 Git 服务器(GitHub、Gitee 或是自建服务器),但只要将源码提交到公网服务器就会存在源码泄漏的风险,而数据库配置信息作为源码的一部分,一旦出现源码泄漏,那么数据库中的所有数据都会公之于众,其产生的不良后果无法预期(比如某某酒店的信息)。

于是为了避免这种问题的产生,我们至少要对数据库的密码进行加密操作,这样即使得到了源码,也不会造成数据的泄露,也算保住了最后一块遮羞布。

如何加密?

对于 Java 项目来说,要想快速实现数据库的加密,最简单可行的方案就是使用阿里巴巴提供的 Druid 来实现加密

什么是Druid?

Druid(中文译为“德鲁伊”)是阿里巴巴开源的一款 Java 语言中最好的数据库连接池。Druid 提供了强大的监控和扩展功能,当然也包含了数据库的加密功能。

Druid 开源地址:https://github.com/alibaba/druid/

Druid可以做什么?

  1. Druid 可以监控数据库访问性能,Druid 内置提供了一个功能强大的 StatFilter 插件,能够详细统计 SQL 的执行性能,这对于线上分析数据库访问性能有帮助。

  2. 替换数据库连接池 DBCP 和 C3P0,Druid 提供了一个高效、功能强大、可扩展性好的数据库连接池。

  3. 数据库密码加密,直接把数据库密码写在配置文件中,这是不好的行为,容易导致安全问题。DruidDruiver 和 DruidDataSource 都支持 PasswordCallback。

  4. SQL 执行日志,Druid 提供了不同的 LogFilter,能够支持 Common-Logging、Log4j 和 JdkLog,你可以按需要选择相应的 LogFilter,监控你应用的数据库访问情况。

  5. 扩展 JDBC,如果你要对 JDBC 层有编程的需求,可以通过 Druid 提供的 Filter-Chain 机制,很方便编写 JDBC 层的扩展插件。

对于本文来说,我们重点来看它的第 3 个特性,也就是使用 Druid 来实现数据库密码加密。

加密执行流程

在没有进行密码加密之前,项目的交互流程是这样的:

在使用了密码加密之后,项目的交互流程就变成了这样:

使用Druid实现加密

本示例运行环境:

Spring Boot 2.4.3 

MySQL 5.7 

Java 1.8 

Idea 2020.1.3

1.添加Druid依赖

Maven 项目:

<dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.5</version>
</dependency>

Gradle 项目:

compile 'com.alibaba:druid-spring-boot-starter:1.2.5'

获取 Druid 最新版本:https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter

2.生成密文

Druid 添加完成之后就可以借助 Druid 中提供的 ConfigTools 类来加密密码了,实现代码如下:

import com.alibaba.druid.filter.config.ConfigTools;class MyTests {public static void main(String[] args) throws Exception {// 需要加密的明文命名String password = "youPassword"; // 【注意:这里要改为你自己的密码】// 调用 druid 生成私钥、公钥、密文ConfigTools.main(new String[]{password});}
}

以上代码执行的结果如下:

privateKey:MIIBUwIBADANBgkqhkiG9w0BAQEFAASCAT0wggE5AgEAAkEApOjcMWSDzJiKVGmtcBBoQPtM9tVW2H2cnS6xZK7NrbzQXYWLQD2zefIrrx9vMvqRIHEqkmAHTuUcUXHgCxu0cwIDAQABAkAlqo5ItdWo0Jqf5zdXJlg5p2yP4HCiqCYyfKzF+2s9KEmgWZJWTctZDsgQ0iYUohORR59I+J4nabhel1x5/INpAiEA6jwSyFqMUPOh1XlrzNFek+RthOQ5n4+ALPo+vULayO0CIQC0O7JM9sIq+tg+jCGv+ypk6vbuRKY9m5W2rSRXapGm3wIgRHul3jAjIDPrF/f1HaAFL+Y0Yws7Ebyp8/yCRWF7iA0CIALbe20q8FMcHPeI4zPWCIsHCpkmb3hEkjAOOKhGIT8DAiAqiUuz92NqKeyjmOfons1ka65EzVwA3NDhZ6+IQcnuig== publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKTo3DFkg8yYilRprXAQaED7TPbVVth9nJ0usWSuza280F2Fi0A9s3nyK68fbzL6kSBxKpJgB07lHFFx4AsbtHMCAwEAAQ== password:IMgKm27bOHok3/+5aDL4jGBoVVZkpicbbM6pIXQppi3dI7h3jngSAqhqwqYnfuYpyVJ0k++q9xWWnHtd6sAWnQ==

从上述结果可以看出,使用 ConfigTools 类会生成 3 部分的内容:

  1. privateKey:私钥,暂时不会用到,用于密码的加密;

  2. publicKey:公钥,用于密码的解密;

  3. password:加密之后的密码。

PS:要实现数据库的加密,主要使用的是 publicKey(公钥)和 password(密文),这就把明文转换成密文了。

3.添加配置

完成了以上操作之后,只需要将上一步生成的公钥密文添加到项目的配置文件 application.yml(或application.xml)中就实现了加密操作了,具体配置信息如下:

spring:# MySQL 配置datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:url: jdbc:mysql://127.0.0.1:3306/testdb?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: rootpassword: IMgKm27bOHok3/+5aDL4jGBoVVZkpicbbM6pIXQppi3dI7h3jngSAqhqwqYnfuYpyVJ0k++q9xWWnHtd6sAWnQ==# encrypt configfilters: configconnect-properties:config.decrypt: trueconfig.decrypt.key: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKTo3DFkg8yYilRprXAQaED7TPbVVth9nJ0usWSuza280F2Fi0A9s3nyK68fbzL6kSBxKpJgB07lHFFx4AsbtHMCAwEAAQ==

其中 password 对应的是上一步生成的 password(密文),而 config.decrypt.key 对应的是上一步生成的 publicKey(公钥),如下图所示:

这里提供一个原始的配置文件,以便和加密后的配置文件进行比对:

4.注意事项-插着钥匙的锁

经过前面 3 步的配置之后,我们的程序就可以正常运行了,但这远没有结束!

在第 3 步配置时,我们将密文和公钥都写入配置文件,这就会造成当有人拿到密文和公钥之后,就可以使用 Druid 将加密的密码还原出来了,这就好比一把插着钥匙的锁是极不安全的。

因此我们正确的使用姿势:是将公钥找一个安全的地方保存起来,每次在项目启动时动态的将公钥设置到项目中,这样就可以有效的保证密码的安全了。

正确的配置文件

接下来我们将 Spring Boot 的公钥设置为配置项,在项目运行时再替换为具体的值,最终的安全配置信息如下:

spring:# MySQL 配置datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.alibaba.druid.pool.DruidDataSourcedruid:url: jdbc:mysql://127.0.0.1:3306/testdb?serverTimezone=Asia/Shanghai&characterEncoding=UTF-8&useSSL=falseusername: rootpassword: IMgKm27bOHok3/+5aDL4jGBoVVZkpicbbM6pIXQppi3dI7h3jngSAqhqwqYnfuYpyVJ0k++q9xWWnHtd6sAWnQ==# encrypt configfilters: configconnect-properties:config.decrypt: trueconfig.decrypt.key: ${spring.datasource.druid.publickey}

可以看出公钥被修改成“${spring.datasource.druid.publickey}”了,这就相当于使用占位符先把坑给占上,等项目启动时再更换上具体的值。

PS:“spring.datasource.druid.publickey”并非是固定不可变的 key,此 key 值用户可自行定义。

开发环境替换公钥

开发环境只需要在 Idea 的启动参数中配置公钥的值即可,如下图所示:

当我们输入正确的公钥值时程序可以正常运行,当输入一个错误的公钥值时就会提示解码失败,如下图所示:

生产环境替换公钥

生产环境在启动 jar 包时只需要动态设置公钥的值即可,参考以下命令:

java -jar xxx.jar --spring.datasource.druid.publickey=你的公钥

Druid运行原理

经过上述步骤之后,我们就完成 MySQL 密码的加密了,这样当 Spring Boot 项目启动时,Druid 的拦截器会使用密文和公钥将密码还原成真实的密码以供项目使用,当然这一切都无需人工干预(无需编写任何代码),Druid 已经帮我封装好了,我们只需要通过以上配置即可。

什么?你想知道 Druid 是如何通过密文和公钥还原出真实的密码的?

没问题,满足你,其实 ConfigTools 类中已经提供了相应实现,代码如下:

// 密文
String password = "VwH1mu2IUpqjfKTd+gSikiZgJTi+3Y5zFIFRfxYnH1UqHzm1K8TIHnMaV3TErBaGsVEaGV0e63pb0Ys3Wdm7Kg==";
// 公钥
String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALWIEp19IM04sB+vQXnEOH9gFNFdL5TFGSEhORgHj4MnfTfBSNaOoSgCaM8BOpjiHmwuEb7LpvmXI1x/ymUvNzECAwEAAQ==";
// 还原成真实的密码
String result = ConfigTools.decrypt(publicKey, password);
System.out.println("最终结果:" + result);

总结

本文我们使用阿里巴巴开源的 Druid 实现了 MySQL 的密码加密,Druid 的加密过程非常方便,无需编写任何代码,只需要添加 Druid 依赖,再通过 Druid 的工具类生成密文,最后将密文配置到 application.yml 文件即可。项目在运行时会通过拦截器将密文转换成真正的密码,从而实现了 MySQL 密码的加密和解码的过程。

最后

原创不易,如果觉得本文对你有帮助,请点个赞再走吧

如果可以,请随手将它分享到你的朋友圈,让更多需要的人知道,谢谢你。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/544943.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机图形学图形旋转_计算机图形学中的平板显示

计算机图形学图形旋转平板显示器 (Flat Panel Display) It is generally known as FPD, the flat-panel display is such a display technology which overtakes Cathode Ray Tube as a new standard of computer desktop displays. Unlike monitors through CRT, flat-panel d…

一文掌握Redisson分布式锁原理|干货推荐

ReentrantLock 重入锁在说 Redisson 之前我们先来说一下 JDK 可重入锁: ReentrantLockReentrantLock 保证了 JVM 共享资源同一时刻只允许单个线程进行操作实现思路ReentrantLock 内部公平锁与非公平锁继承了 AQS[AbstractQueuedSynchronizer]1、AQS 内部通过 volatil 修饰的 in…

android创建文件夹和文件的一些经验教训

这几天做一个功能需要在手机上创建一个文件夹&#xff0c;然后往里面存储一些文件&#xff0c;首先得考虑用户有没有sdcard&#xff0c;如果有就在sdcard上创建一个指定的文件夹&#xff0c;如果没有则在你的工程所在的目录“/data/data/你的包名”下创建文件夹。用到的方法是&…

7种分布式事务的解决方案,一次讲给你听

本文约5300字&#xff0c;阅读时长「5分钟」什么是分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器「分别位于不同的分布式系统的不同节点之上」。一个大的操作由N多的小的操作共同完成。而这些小的操作又分布在不同的服务上。针对于这些操…

js中的转译_JavaScript中的填充和转译

js中的转译JavaScript is rapidly advancing. Today its the most popular programming/scripting language that devs use to code logic and applications and is used in umpteen number of places. The community behind its development hasnt stopped creating and addin…

css @media 响应式布局

2019独角兽企业重金招聘Python工程师标准>>> &#xfeff;1、在 html 标签中 <link rel"stylesheet" type"text/css" href"style1.css" media"screen and (min-width: 600px) and (max-width: 800px)"> 2、在样式表中…

Apache JK Tomcat 集群问题

2019独角兽企业重金招聘Python工程师标准>>> 这几天被集群并发问题快折腾死了&#xff0c;望哪位高人看下到底是哪里出现了问题。 Apache Server是正常的&#xff0c;各服务器的Tomcat 也是正常的&#xff0c;但当Apache的连接数达到 300左右的时候&#xff0c;JK就…

Redis实现分布式锁的7种方案,及正确使用姿势!

种方案前言日常开发中&#xff0c;秒杀下单、抢红包等等业务场景&#xff0c;都需要用到分布式锁。而Redis非常适合作为分布式锁使用。本文将分七个方案展开&#xff0c;跟大家探讨Redis分布式锁的正确使用方式。如果有不正确的地方&#xff0c;欢迎大家指出哈&#xff0c;一起…

c#中的long类型示例_C#中带示例的无符号字节数组

c#中的long类型示例C&#xff03;中的无符号字节数组 (Unsigned Byte Array in C#) In C#.Net, we can create an unsigned byte array by using byte, byte is used to store only positive values between the range of 0 to 255 (Unsigned 8 bits integer). 在C&#xff03;…

Android软件开发之盘点所有Dialog对话框大合集(一)

转&#xff1a;http://xys289187120.blog.51cto.com/3361352/657562/ 雨松MOMO带大家盘点Android 中的对话框 今天我用自己写的一个Demo 和大家详细介绍一个Android中的对话框的使用技巧。 1.确定取消对话框 对话框中有2个按钮 通过调用 setPositiveButton 方法 和 setNegat…

JDK 9 对字符串 String 的优化,挺有意思!

String类可以说是Java编程中使用最多的类了&#xff0c;如果能对String字符串的性能进行优化&#xff0c;那么程序的性能必然能大幅提升。这不JDK9就对String字符串进行了改进升级&#xff0c;在某些场景下可以让String字符串内存减少一半&#xff0c;进而减少JVM的GC次数。Str…

PHP将数组存入数据库中的四种方式

PHP将数组存入数据库中的四种方式 最近突然遇到了一个问题&#xff0c;如何用PHP将数组存入到数据库中&#xff0c;经过自己的多方查找和研究&#xff0c;总结了以下四种方法&#xff1a;1.implode()和explode()方式2.print_r()和自定义函数方式3.serialize()和unserialize()方…

c#中.clear()作用_清单 .Clear()方法以及C#中的示例

c#中.clear()作用C&#xff03;List <T> .Clear()方法 (C# List<T>.Clear() Method) List<T>.Clear() method is used to clear the list, it remove all elements from the list. List <T> .Clear()方法用于清除列表&#xff0c;它从列表中删除所有元…

Android开发:利用Activity的Dialog风格完成弹出框设计

转&#xff1a;http://www.linuxidc.com/Linux/2011-08/41933.htm 在我们使用Dialog时&#xff0c;如果需要用到很多自己设计的控件&#xff0c;虽然可以让弹出框显示出我们需要的界面&#xff0c;但却无法找到地方完成控制代码的编写&#xff0c;如何解决这个问题呢,我们可以将…

Java中实现定时任务的3种方法!

今天我们不用任何框架&#xff0c;用最朴素的 Java API 来实现定时任务&#xff0c;本文会介绍 3 种实现方案&#xff0c;我们一起来看...1、 sleep 这也是我们最常用的 sleep 休眠大法&#xff0c;不只是当作休眠用&#xff0c;我们还可以利用它很轻松的能实现一个简单的定时任…

回文子序列_计算回文子序列的总数

回文子序列Problem statement: 问题陈述&#xff1a; Given a string str, find total number of possible palindromic sub-sequences. A sub-sequence does not need to be consecutive, but for any xixj i<j must be valid in the parent string too. Like "icl&q…

【Microsoft Azure学习之旅】测试消息队列(Service Bus Queue)是否会丢消息

组里最近遇到一个问题&#xff0c;微软的Azure Service Bus Queue是否可靠&#xff1f;是否会出现丢失消息的情况&#xff1f; 具体缘由如下&#xff0c; 由于开发的产品是SaaS产品&#xff0c;为防止消息丢失&#xff0c;跨Module消息传递使用的是微软Azure消息队列&#xff0…

使用SharedPreferences存储和读取数据

转&#xff1a;http://www.worlduc.com/blog2012.aspx?bid19403392 1、任务目标 &#xff08;1&#xff09;掌握Android中SharedPreferences的使用方法。 2、任务陈述 &#xff08;1&#xff09;运行后&#xff0c;显示如下界面&#xff0c;可以写入和读取SharedPreferences中…

Zookeeper 的 5 大核心知识点!

1 ZooKeeper简介ZooKeeper 是一个开源的分布式协调框架&#xff0c;它的定位是为分布式应用提供一致性服务&#xff0c;是整个大数据体系的管理员。ZooKeeper 会封装好复杂易出错的关键服务&#xff0c;将高效、稳定、易用的服务提供给用户使用。如果上面的官方言语你不太理解&…

PHP | 计算字符串中的单词总数

Given a string and we have to count the total number of words in it. 给定一个字符串&#xff0c;我们必须计算其中的单词总数。 str_word_count() function str_word_count()函数 To find the total number of words in a string, we can use str_word_count() function…