SpringBoot使用Druid数据库加密链接完整方案
网上的坑
springboot 使用 Druid 数据库加密链接方案,不建议采用网上的一篇文章《springboot 结合 Druid 加密数据库密码遇到的坑!》介绍的方式来进行加密链接实现。本文章下文分享 Druid 源码后就知道为什么不建议采用该方式的原因了。
加密准备
首先使用 CMD 生成数据库加密字符串,该命令会产生三个值 privateKey=公钥、publicKey=密码加密后的结果、password=密码加密串
java -cp druid-1.0.28.jar com.alibaba.druid.filter.config.ConfigTools pcds123123456
使用 Durid 的工具类 ConfigTools 验证加密字符串
@Test
public void db_decrypt_test() throws Exception {
String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJggkaRJ+bqLMF6pefubEDLViboxYKGTdGe+78DziIta8Nv8crOA83M0tFG8y8CqHcFYIbG89q9zcnNvL+E2/CECAwEAAQ==";
String password = "AgDRyKJ81Ku3o0HSyalDgCTtGsWcKz3fC0iM5pLur2QJnIF+fKWKFZ6c6e36M06tF2uCadvS/EodWxmRDWwvIA==";
System.out.println(ConfigTools.decrypt(publicKey, password));
}
实现方案
SpringBoot 集成 Druid 数据库链接加密的方式,推荐使用配置注入方式初始化 DataSource。方法如下:
一、增加配置注入 Bean
import java.sql.SQLException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import com.alibaba.druid.pool.DruidDataSource;
@Configuration
@ConfigurationProperties(prefix = "spring.datasource")
public class DbConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(DbConfig.class);
private String url;
private String driverClassName;
private String username;
private String password;
private Integer initialSize;
private Integer minIdle;
private Integer maxActive;
private Integer maxWait;
private Integer timeBetweenEvictionRunsMillis;
private Integer minEvictableIdleTimeMillis;
private String validationQuery;
private Boolean testWhileIdle;
private Boolean testOnBorrow;
private Boolean testOnReturn;
private Boolean poolPreparedStatements;
private Integer maxOpenPreparedStatements;
private Integer maxPoolPreparedStatementPerConnectionSize;
private String filters;
private String publicKey;
private String connectionProperties;
@Primary
@Bean(name = "dataSource")
public DataSource dataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(url);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
datasource.setConnectionProperties(connectionProperties.replace("${publicKey}", publicKey));
try {
datasource.setFilters(filters);
} catch (SQLException e) {
LOGGER.error("druid configuration initialization filter", e);
}
return datasource;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getUsername() {
return username;
}
publicvoid setUsername(String username){
this.username = username;
}
publicString getPassword(){
return password;
}
publicvoid setPassword(String password){
this.password = password;
}
publicInteger getInitialSize(){
return initialSize;
}
publicvoid setInitialSize(Integer initialSize){
this.initialSize = initialSize;
}
publicInteger getMinIdle(){
return minIdle;
}
publicvoid setMinIdle(Integer minIdle){
this.minIdle = minIdle;
}
publicInteger getMaxActive(){
return maxActive;
}
publicvoid setMaxActive(Integer maxActive){
this.maxActive = maxActive;
}
publicInteger getMaxWait(){
return maxWait;
}
publicvoid setMaxWait(Integer maxWait){
this.maxWait = maxWait;
}
publicInteger getTimeBetweenEvictionRunsMillis(){
return timeBetweenEvictionRunsMillis;
}
publicvoid setTimeBetweenEvictionRunsMillis(Integer timeBetweenEvictionRunsMillis){
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
publicInteger getMinEvictableIdleTimeMillis(){
return minEvictableIdleTimeMillis;
}
publicvoid setMinEvictableIdleTimeMillis(Integer minEvictableIdleTimeMillis){
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
publicString getValidationQuery(){
return validationQuery;
}
publicvoid setValidationQuery(String validationQuery){
this.validationQuery = validationQuery;
}
publicBoolean getTestWhileIdle(){
return testWhileIdle;
}
publicvoid setTestWhileIdle(Boolean testWhileIdle){
this.testWhileIdle = testWhileIdle;
}
publicBoolean getTestOnBorrow(){
return testOnBorrow;
}
publicvoid setTestOnBorrow(Boolean testOnBorrow){
this.testOnBorrow = testOnBorrow;
}
publicBoolean getTestOnReturn(){
return testOnReturn;
}
publicvoid setTestOnReturn(Boolean testOnReturn){
this.testOnReturn = testOnReturn;
}
publicBoolean getPoolPreparedStatements(){
return poolPreparedStatements;
}
publicvoid setPoolPreparedStatements(Boolean poolPreparedStatements){
this.poolPreparedStatements = poolPreparedStatements;
}
publicInteger getMaxOpenPreparedStatements(){
return maxOpenPreparedStatements;
}
publicvoid setMaxOpenPreparedStatements(Integer maxOpenPreparedStatements){
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}
publicInteger getMaxPoolPreparedStatementPerConnectionSize(){
return maxPoolPreparedStatementPerConnectionSize;
}
publicvoid setMaxPoolPreparedStatementPerConnectionSize(Integer maxPoolPreparedStatementPerConnectionSize){
this.maxPoolPreparedStatementPerConnectionSize = maxPoolPreparedStatementPerConnectionSize;
}
publicString getFilters(){
return filters;
}
publicvoid setFilters(String filters){
this.filters = filters;
}
publicString getPublicKey(){
return publicKey;
}
publicvoid setPublicKey(String publicKey){
this.publicKey = publicKey;
}
publicString getConnectionProperties(){
return connectionProperties;
}
publicvoid setConnectionProperties(String connectionProperties){
this.connectionProperties = connectionProperties;
}}
二、增加配置文件
# mysql config
spring.datasource.name=pcdsdata
spring.datasource.url=jdbc:mysql://192.168.1.1/mydb
spring.datasource.username=root
spring.datasource.password=AgDRyKJ81Ku3o0HSyalDgCTtGsWcKz3fC0iM5pLur2QJnIF+fKWKFZ6c6e36M06tF2uCadvS/EodWxmRDWwvIA==
# druid datasource config
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.maxActive=20
spring.datasource.initialSize=1
spring.datasource.minIdle=1
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 1 from dual
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=config,stat,wall,log4j
spring.datasource.publicKey=MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJggkaRJ+bqLMF6pefubEDLViboxYKGTdGe+78DziIta8Nv8crOA83M0tFG8y8CqHcFYIbG89q9zcnNvL+E2/CECAwEAAQ==
spring.datasource.connectionProperties=config.decrypt=true;config.decrypt.key=${spring.datasource.publicKey}
注意事项
1)config.decrypt=true;config.decrypt.key= 该两项配置为 Druid 内部固定 Key。网上很多案例配置为 publicKey,这是有问题的。
2)其他教程会配置 DbType 为 spring.datasource.type=com.alibaba.druid.pool.DruidDataSource 实际在 Druid 源码中会判断 DbType 为 MySql 或者 Db2 等类型。可删除此项 原逻辑将从 this.dbType = JdbcUtils.getDbType(jdbcUrl, null);//数据库链接字符串 获取 DbType 值
经过以上配置,SpringBoot 使用 Druid 加密链接完成。如果过程中遇到其他问题可在下方留言,方便更多人解决类似问题。
源码分析
1) 根据源码逻辑,如果不自定义注入 Bean, 在 SpringBoot 初始化时只会读取系统配置参数,如下
public DruidDataSource(){
this(false);
}
public DruidDataSource(boolean fairLock){
super(fairLock);
configFromPropety(System.getProperties());
}
public void configFromPropety(Properties properties) {
{
Boolean value = getBoolean(properties, "druid.testWhileIdle");
if (value != null) {
this.setTestWhileIdle(value);
}
}
{
Boolean value = getBoolean(properties, "druid.testOnBorrow");
if (value != null) {
this.setTestOnBorrow(value);
}
}
...
2) 所有的配置在类 DruidAbstractDataSource 中,自定义注入 Bean,将在这个类中设置相关属性
3) 在首次初始化数据库链接时将调用 DruidDataSource.init(),并进入到 ConfigFilter.init ,初始化建立链接。根据配置config.decrypt 决定是否要进行解密动作,如需解密则加载config.decrypt.key 和 password(首先加载 connectionProperties 链接字符串中的 password,没有再加载默认的 spring.datasource.password) ConfigFilter.decrypt 执行解密动作。
4) 文章《springboot 结合 Druid 加密数据库密码遇到的坑!》中其实是绕过了是否要进行加密等配置,自己实现了解密动作再把数据库链接密码替换掉,这违背了 Druid 设计的初衷了。
参考资料
参考资料 https://github.com/alibaba/druid/wiki
SpringBoot使用Druid数据库加密链接完整方案的更多相关文章
- Android数据库加密之sqlciher方案
版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/6241964.html 前言 大家好,我是Cavalier ...
- springboot+mybatisplus+druid数据库
1.添加maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis ...
- 解决springboot druid 数据库批量更新错误问题
原文:https://www.2cto.com/kf/201712/706399.html springboot druid 数据库多SQL错误multi-statement not allow Ca ...
- Spring Boot 配置文件密码加密两种方案
Spring Boot 配置文件密码加密两种方案 jasypt 加解密 jasypt 是一个简单易用的加解密Java库,可以快速集成到 Spring 项目中.可以快速集成到 Spring Boot 项 ...
- 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务
文章来自: https://blog.csdn.net/qq_29242877/article/details/79033287 在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据 ...
- springboot+mybatis+druid+atomikos框架搭建及测试
前言 因为最近公司项目升级,需要将外网数据库的信息导入到内网数据库内.于是找了一些springboot多数据源的文章来看,同时也亲自动手实践.可是过程中也踩了不少的坑,主要原因是我看的文章大部分都是s ...
- 基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建
基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建 前言 最近做回后台开发,重新抓起以前学过的SSM(Spring+Sp ...
- springboot配置Druid数据源
springboot配置druid数据源 Author:SimpleWu springboot整合篇 前言 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringDa ...
- 基于spring-boot的应用程序的单元+集成测试方案
目录 概述 概念解析 单元测试和集成测试 Mock和Stub 技术实现 单元测试 测试常规的bean 测试Controller 测试持久层 集成测试 从Controller开始测试 从中间层开始测试 ...
随机推荐
- http协议与https协议的前世今生
一.Http与Https的区别: HTTP 的URL 以http:// 开头,而HTTPS 的URL 以https:// 开头 HTTP 是不安全的,而 HTTPS 是安全的 HTTP 标准端口是80 ...
- linux 安装node.js 和npm
cd /usr/local mkdir nodejs cd nodejs 去https://nodejs.org/en/download/复制安装包地址 wget https://nodejs.org ...
- Thread类中start()方法喝run()方法有什么不同?
答:当调用start()方法时会启动一个新创建的线程,然后在start()内部调用run()方法.这和直接调用run()方法不同.直接调用run()方法只是在原来的线程中调用,没有创建新的线程.只有调 ...
- Spring事务操作介绍
Spring的特色之一,简单而强大的事务管理功能,包括编程式事务和声明式事务. 1. Spring中涉及到事务管理的API有100多个,核心的只有三个: TransactionDefinition.P ...
- ansible-play中for,if的使用
#迭代循环的使用 #实现同时新建三个文件,同时部署三个服务 --- - host: websrvs remote_user: root task: - name: create some files ...
- 如何用jmeter进行数据库性能测试
由于业务需要,需要进行数据库性能测试,记录过程进行学习 测试前期准备: 1.测试点准备及需求点 a.性能测试目的 b.jmeter测试数据库基本脚本 get c.数据库性能监控工具选择 d.服务器性 ...
- BigDecimal类的用法
(一)BigDecimal类的常用的几个构造方法 BigDecimal(int):将int表示形式转换为BigDecimal对象 BigDecimal(String):将字符串表示形式转换为BigDe ...
- 常见图片格式PNG,JPEG,BMP,GIF区别总结
在前端工作久了经常会遇到各种格式的图片文件,现文做一些区别总结,帮助理解但不深入. [PNG](Portable Network Graphics) PNG是一种无损压缩的位图图形格式,主要有PNG8 ...
- 马凯军201771010116《面向对象与程序设计Java》第十七周学习总结
一.理论知识部分 Java 的线程调度采用优先级策略:优先级高的先执行,优先级低的后执行:多线程系统会自动为每个线程分配一个优先级,缺省时,继承其父类的优先级: 任务紧急的线程,其优先级较高: 同优先 ...
- TCP长连接与短连接、心跳机制
1. TCP连接 当网络通信时采用TCP协议时,在真正的读写操作之前,server与client之间必须建立一个连接,当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,连接的建立是需要三次 ...