zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    delphi7 projectoptions打开出错
    file not found frmaddBdsuo.dcu
    session 不活动是因为未注册 WebSessionActivator,或试图在 IHttpHandler 的构造函数中 访问session
    oracle导出指定的表,并将指定的表追加到其他dmp文件中(不影响dmp中其他的表)
    父类指针转换成子类指针
    为什么会是这样的输出结果
    字符数组
    变量的声明和定义
    const对象默认为文件的局部变量
    输出结果
  • 原文地址:https://www.cnblogs.com/arraylist/p/10209325.html
Copyright © 2011-2022 走看看