网上的坑

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数据库加密链接完整方案的更多相关文章

  1. Android数据库加密之sqlciher方案

    版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请表明出处:http://www.cnblogs.com/cavalier-/p/6241964.html 前言 大家好,我是Cavalier ...

  2. springboot+mybatisplus+druid数据库

    1.添加maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis ...

  3. 解决springboot druid 数据库批量更新错误问题

    原文:https://www.2cto.com/kf/201712/706399.html springboot druid 数据库多SQL错误multi-statement not allow Ca ...

  4. Spring Boot 配置文件密码加密两种方案

    Spring Boot 配置文件密码加密两种方案 jasypt 加解密 jasypt 是一个简单易用的加解密Java库,可以快速集成到 Spring 项目中.可以快速集成到 Spring Boot 项 ...

  5. 3分钟搞定SpringBoot+Mybatis+druid多数据源和分布式事务

    文章来自: https://blog.csdn.net/qq_29242877/article/details/79033287 在一些复杂的应用开发中,一个应用可能会涉及到连接多个数据源,所谓多数据 ...

  6. springboot+mybatis+druid+atomikos框架搭建及测试

    前言 因为最近公司项目升级,需要将外网数据库的信息导入到内网数据库内.于是找了一些springboot多数据源的文章来看,同时也亲自动手实践.可是过程中也踩了不少的坑,主要原因是我看的文章大部分都是s ...

  7. 基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建

    基于Maven的Springboot+Mybatis+Druid+Swagger2+mybatis-generator框架环境搭建 前言 最近做回后台开发,重新抓起以前学过的SSM(Spring+Sp ...

  8. springboot配置Druid数据源

    springboot配置druid数据源 Author:SimpleWu springboot整合篇 前言 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringDa ...

  9. 基于spring-boot的应用程序的单元+集成测试方案

    目录 概述 概念解析 单元测试和集成测试 Mock和Stub 技术实现 单元测试 测试常规的bean 测试Controller 测试持久层 集成测试 从Controller开始测试 从中间层开始测试 ...

随机推荐

  1. Django数据同步过程中遇到的问题:

    1.raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__ve ...

  2. ansible 剧本

    ansible的管理与剧本   首先我们安装一个ansible. 在7版本,直接用yum安装就可以 yum -y install ansible 然后清空ansible的配置文件,在里面写入自己需要管 ...

  3. unity重写软键盘for Android NGUI

    一 原因 以为某些原因,需要重写Unity游戏中Android的软键盘的样式,然而unity对android和ios的ios的软键盘都有封装,在Unity中TouchScreenKeyboard.Op ...

  4. java 反射与其应用

    前言: 目标:对于我个人而言 我希望能由以下途径达到最终目标  清楚反射的概念以及为什么要用反射技术 -> java的反射技术 -> java反射技术涉及到的类 -> java反射技 ...

  5. 1.3 SQL循环

    1.while循环(1~20的和) 2.while_break_continue(1~20偶数和) 3.if选择象限 4.return:在查询中无条件退出,return后面的语句将不会被执行. 5.g ...

  6. MySQL简单的查询语句

    1.查询特定列:select 列名 from 表名:(必须先进入数据库)或者 select 列名 from 数据库.表名: 2.查询多个列:select 列1,列2,... from 表名: 3.除去 ...

  7. 自动化运维之Saltstack

    第三十八课 自动化运维之Saltstack 目录 一.自动化运维介绍 二. saltstack安装 三. 启动saltstack服务 四. saltstack配置认证 五. saltstack远程执行 ...

  8. alibaba/druid 下的 密码加密

    使用ConfigFilter cliangch edited this page on 3 Feb · 12 revisions ConfigFilter的作用包括: 从配置文件中读取配置 从远程ht ...

  9. oracle入门之对表数据查询(二)

    oracle表复杂查询 在实际应用中经常需要执行复杂的数据统计,经常需要显示多张表的数据,现在我们给大家介绍较为复杂的select语句 数据分组-max,min,avg,sum,count 如何显示所 ...

  10. SQLZOO网页中SQL的答案(SELECT from world篇)

    SELECT from world篇 11. 题目: The CASE statement shown is used to substitute North America forCaribbean ...