背景

SpringBoot 项目,使用 Druid 自动装配的数据源,数据源的帐号密码配置加密后,如何完成数据源的装配呢?

druid-spring-boot-starter 虽然自带了加密配置,但是密钥也是配置的,如果需要用自定义的加密解密工具,如果不用自带的工具,怎么自定义实现加密数据源的装配呢?

本文从 DruidDataSourceAutoConfigure 类源码入手,仿造该类,自定义一个数据源注入配置,在真正注入 DruidDataSource 之前,对 druid 配置信息完成解密。

主要思考三个问题:

  • 自定的 Configuration 类中的 @Bean 注入一个 DruidDataSource ,为什么比自动装配的时机早呢?
  • 如果自定义一个自动装配类, 包含DataSourceProperties 属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure 类之前装配,怎么实现呢?
  • 自动装配类的工作原理是什么?注入优先级怎么确定的?

加密数据源自主实现流程

Not registered via @EnableConfigurationProperties, 
marked as Spring component, 
or scanned via @ConfigurationPropertiesScan 

@ConfigurationProperties 用法限制,我想到一个解决办法,为当前类加上 @Component,同时制定一个不可能的注入条件:@ConditionalOnProperty(prefix = "xx",name = "xxx", havingValue = "impossible")

不用官方的加密插件,自定义 Druid 的解密配置,我想到的方法是完全仿照 Druid 数据源的自动装配过程,改写 DruidDataSource 的注入过程。

关键是修改 DataSourceProperties 这个类的实例的帐号密码属性,其他完全照搬 DruidDataSourceAutoConfigure 实现即可。

第一步,由于 Druid 自动注入的数据源 DruidDataSourceWrapper 是一个包内类,不能直接拿来用,所以完全拷贝一份这个类,定义为咱们自己的数据源类:

@Component
@ConfigurationProperties("spring.datasource.druid")
@ConditionalOnProperty(prefix = "spring.datasource",name = "encrypted", havingValue = "impossible")
public class MyEncryptedDatasourceWrapper extends DruidDataSource implements InitializingBean {
    @Autowired
    private DataSourceProperties basicProperties;
    public MyEncryptedDatasourceWrapper() {
    }
    @Override
    public void afterPropertiesSet() {
        if (super.getUsername() == null) {
            super.setUsername(this.basicProperties.determineUsername());
        }
        if (super.getPassword() == null) {
            super.setPassword(this.basicProperties.determinePassword());
        }
        if (super.getUrl() == null) {
            super.setUrl(this.basicProperties.determineUrl());
        }
        if (super.getDriverClassName() == null) {
            super.setDriverClassName(this.basicProperties.getDriverClassName());
        }
    }
    @Autowired(
            required = false
    )
    public void autoAddFilters(List<Filter> filters) {
        super.filters.addAll(filters);
    }
    @Override
    public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
        try {
            super.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);
        } catch (IllegalArgumentException var4) {
            super.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
        }
    }
}

第二步,自定义一个 DruidDataSourceAutoConfigure 类,内容与该类一样,但是多一个数据源配置属性:

@Configuration
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class, DruidFilterConfiguration.class})
public class MyEncryptedDatasourceWrapperConfig {
    /**
     * 该属性封装了 spring.datasource 属性,需要对它的帐号、密码属性进行解密
     */
    @Autowired
    private DataSourceProperties basicProperties;
    /**
     * 使用数据源配置信息,解密帐号和密码后创建数据库连接池
     * @return
     */
    @Bean
    public DataSource dataSource() {
        // TODO 对密码解密并设置回去
        basicProperties.setPassword(password);
        return new MyEncryptedDatasourceWrapper();
    }
}

这样就完成了 Spring druid 数据源配置的解密处理了。

基础巩固

boolean proxyBeanMethods() 默认值是 true. 从这个成员变量的注释中,我们可以看到一句话 Specify whether {@code @Bean} methods should get proxied in order to enforce bean lifecycle behavior, e.g. to return shared singleton bean instances even in case of direct {@code @Bean} method calls in user code.

其实从这句话,我们就可以初步得到我们想要的答案了:在带有 @Configuration 注解的类中,一个带有 @Bean 注解的方法显式调用另一个带有 @Bean 注解的方法,返回的是共享的单例对象。

参考文档:《Component 和 Configuration 区别》

额外尝试

加密数据源配置的解密流程,核心在 DataSourceProperties 这个实例装配完成后修改密码信息,尝试自定义一个 @Configuration@Bean 注入一个 DataSourceProperties 实例,但是这个对象到了 Druid 自动注入类那里,属性还是没有发生变化:

@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class MyAutoConfig {
    @Autowired
    private DataSourceProperties basicProperties;
    public MyAutoConfig() {
        System.out.println("My Auto config");
    }
    @Bean
    public DataSourceProperties basicProperties() {
        // TODO 解密配置
        System.out.println("username "   basicProperties.getUsername());
        return basicProperties;
    }
}

这里 @Bean 注入生效了,到了 DruidDataSource 那使用的也是这个实例,单步调试对象地址是一样的,但是改的属性没有生效。

数据源实例化配置时引用的属性:

但是两个地方的属性却不同,前一步解密的信息并没有传递到真正使用的地方。 理论上,同一个对象,前面修改了属性,这里同一个线程里面,属性应该变化了才对呢!不得其解。

启示录

回顾开头的三个问题:

  • 自定的 Configuration 类中的 @Bean 注入一个 DruidDataSource ,为什么比自动装配的时机早呢?因为 @Bean 属于当前项目扫描路径,它里面的类注入优先级高于第三方 jar 包中的 spring.factories 的装配类。
  • 如果自定义一个自动装配类, 包含DataSourceProperties 属性,对它的帐号密码解密后,让它在DruidDataSourceAutoConfigure 类之前装配,怎么实现呢?尝试定义一个装配类 @Bean 装配一个数据源 DataSourceProperties 对象,并修改配置。
  • 自动装配类的工作原理是什么?注入优先级怎么确定的?spring.factories 的本质是 SPI,它针对的是第三方 jar 包,不需要手动配置扫描路径,又需要自动注入的情况,是各种 starter 定义底层实现途径。

优先级:本地扫描路径的 Configuration -> 实现了 BeanDefinitionRegistryPostProcessor 接口的类 -> spring.factories 中的自动装配类。

以上就是拦截Druid数据源自动注入帐密解密实现详解的详细内容,更多关于Druid注入帐密解密拦截的资料请关注Devmax其它相关文章!

拦截Druid数据源自动注入帐密解密实现详解的更多相关文章

  1. PHP rsa加密解密算法原理解析

    这篇文章主要介绍了PHP rsa加密解密算法原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

  2. php实现基于openssl的加密解密方法

    这篇文章主要介绍了php实现基于openssl的加密解密方法,结合实例形式分析了php自定义函数实现基于openssl的加密解密操作相关技巧,需要的朋友可以参考下

  3. 详解如何在Java中加密和解密zip文件

    在本文中,我们来学习如何用Zip4j库创建受密码保护的压缩文件并将其解压,文中的示例代码讲解详细,具有一定的借鉴价值,需要的可以参考一下

  4. PHP代码加密和扩展解密实战

    这篇文章主要介绍了PHP代码加密和扩展解密实战,对加密解密感兴趣的同学,可以参考下

  5. PHP实现支持加盐的图片加密解密

    PHP加密解密算是老话题,今天给大家分先一篇关于php实现图片加密解密,支持加盐的文章,有需要的朋友们可以参考借鉴。

  6. php加密解密字符串示例

    加密解密在一个系统中的应用是非常常见的需求,PHP做的网站中,也会经常使用的PHP加密解密字符串函数.

  7. php实现的三个常用加密解密功能函数示例

    这篇文章主要介绍了php实现的三个常用加密解密功能函数,涉及php针对字符串的遍历、截取、编码转换等相关操作技巧,需要的朋友可以参考下

  8. 简单的Python解密rsa案例

    这篇文章主要为大家介绍了简单的Python解密rsa案例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  9. python实现凯撒密码加密解密的示例代码

    本文主要介绍了python实现凯撒密码加密解密的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  10. uniapp小程序使用RSA加密解密的操作代码

    这篇文章主要介绍了uniapp小程序使用RSA加密解密,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部