spring cloud与加密库jasypt(ulisesbocchio)冲突问题定位
背景
最近在项目上遇到个问题。项目就是普通的spring cloud,spring cloud在spring boot的基础上多了一些东西,比如支持bootstrap上下文(通过bootstrap.yml/properties配置)。另外呢,我们这边要求上线的时候,要把配置文件里的敏感配置如密码,进行加密。加密的话,我们这边用了如下库:
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
https://github.com/ulisesbocchio/jasypt-spring-boot
加密后,配置文件里敏感属性就长这样:
secret.property=ENC(nrmZtkF7T0kjG/VodDvBw93Ct8EgjCA+)
程序启动时,会加载配置文件,发现值是ENC()格式,就会自动解密为明文。
这次,在如下这种场景中,遇到问题了:
本来没在pom.xml中引入这个包的时候,一切正常;引入后,直接启动都启动不起来了,(注意,我还没开始用这个包的ENC加密那些功能呢),报错大概如下:
Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
是提示找不到url,感觉我的数据库配置没生效一样。
这是怎么一回事呢?
问题定位过程
检查datasourceProperties
发现这个配置类有问题,全空。
检查Binder部分
一般来说,这种配置类都长下面这种:
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties{
private String url;
...
}
这个类会被注册到spring应用上下文内,成为一个bean,这部分是通过EnableConfigurationProperties
来实现,它会把value对应的class,注册为一个bean:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EnableConfigurationPropertiesRegistrar.class)
public @interface EnableConfigurationProperties {
Class<?>[] value() default {};
}
上面类中,import的EnableConfigurationPropertiesRegistrar
会负责将这些配置类注册到spring:
注册为bean后,DataSourceProperties这个bean中的属性又是从何而来呢,这个就是靠从外部配置文件获取了,如我们这里的application-dev.yaml:
spring:
application:
name: property-encrypt-bug
datasource:
url: jdbc:mysql://1.1.1.1:3306
username: root
这部分功能靠如下类实现:
这是一个bean的后置处理器,它是在bean进行初始化之前,来做这件事。
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
bind(ConfigurationPropertiesBean.get(this.applicationContext, bean, beanName));
return bean;
}
我们这里打个条件断点:beanName.contains("datasource")
最终来到一个遍历bean中每个属性的地方:
具体到每一个属性呢,则是会遍历一个context.getSources来查找这个属性的值。
值得注意的是,上面的context.getSources中,其实共包含了9个propertySource,这边给大家看下:
但是,这里有个问题是,好像没有我们的application-dev.yml呢?如果这里没有application-dev.yml,那自然是找不到我们的配置了。
我然后做了个对照组,看看没有引入加密包的时候,这里是什么值?
果然不一样。
那就看看这个是怎么来的:
@Override
public Iterable<ConfigurationPropertySource> getSources() {
if (this.sourcePushCount > 0) {
return this.source;
}
return Binder.this.sources;
}
private Iterable<ConfigurationPropertySource> getConfigurationPropertySources() {
return ConfigurationPropertySources.from(this.propertySources);
}
这里就可以看到,是从spring中获取了PropertySourcesPlaceholderConfigurer
类型的bean,那么,PropertySourcesPlaceholderConfigurer
中的数据从哪来呢?
@Nullable
private PropertySources appliedPropertySources;
public PropertySources getAppliedPropertySources() throws IllegalStateException {
Assert.state(this.appliedPropertySources != null, "PropertySources have not yet been applied");
return this.appliedPropertySources;
}
而appliedPropertySources字段,则是在方法:PropertySourcesPlaceholderConfigurer#postProcessBeanFactory
中赋值的,里面的逻辑是从environment这个bean中获取propertySource。
所以,最终其实还是environment有问题。
检查environment部分
接上文,看看environment的实际类型:
class ApplicationServletEnvironment extends StandardServletEnvironment {
private final MutablePropertySources propertySources;
}
这个字段,在正常情况下,包含了applicaion-dev.yml:
异常情况下则没有。
所以,问题就变成了,为什么在异常情况下,environment没有这个yaml。
再进一步跟踪下,发现在默认创建environment时,刚开始的时候,里面只会包含几个初始的:
org.springframework.context.support.AbstractApplicationContext#getEnvironment
@Override
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
this.environment = createEnvironment();
}
return this.environment;
}
这就有点难搞了,剩下的是啥时候弄进去的呢?
由于这是一个列表,往里面放、取、replace的操作都是可能的,要找到所有这些入口,不那么简单了。
我是在每个增、删、set等方法,全打了断点,一个一个看,但是这种办法也很头疼,稍微错过一个入口,就发现,list的内容变了,还不知道在哪里改动的。
后面我是采用这种方式 + 对照组相结合的方式来debug。
经过长时间的对照和调试,最终才找到了如下位置:
在spring cloud中,我们说会存在bootstrap上下文的创建,bootstrap的处理是在:
org.springframework.cloud.bootstrap.BootstrapApplicationListener#onApplicationEvent
这个类的大体处理逻辑如下:
在environment准备好之后,就会触发上述逻辑,上述逻辑中,会创建bootstrap对应的spring应用上下文容器,见红框标出处。
后面发现,在org.springframework.boot.env.EnvironmentPostProcessorApplicationListener#onApplicationEnvironmentPreparedEvent
处,会添加进去bootstrap.yaml这个配置:
再后来,发现在如下处添加了application-dev.yml,完整堆栈如下:
addLast:116, MutablePropertySources (org.springframework.core.env)
applyContributor:352, ConfigDataEnvironment (org.springframework.boot.context.config)
applyToEnvironment:329, ConfigDataEnvironment (org.springframework.boot.context.config)
processAndApply:233, ConfigDataEnvironment (org.springframework.boot.context.config)
postProcessEnvironment:102, ConfigDataEnvironmentPostProcessor (org.springframework.boot.context.config)
postProcessEnvironment:94, ConfigDataEnvironmentPostProcessor (org.springframework.boot.context.config)
onApplicationEnvironmentPreparedEvent:102, EnvironmentPostProcessorApplicationListener (org.springframework.boot.env)
onApplicationEvent:87, EnvironmentPostProcessorApplicationListener (org.springframework.boot.env)
doInvokeListener:178, SimpleApplicationEventMulticaster (org.springframework.context.event)
invokeListener:171, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:145, SimpleApplicationEventMulticaster (org.springframework.context.event)
multicastEvent:133, SimpleApplicationEventMulticaster (org.springframework.context.event)
environmentPrepared:85, EventPublishingRunListener (org.springframework.boot.context.event)
lambda$environmentPrepared$2:66, SpringApplicationRunListeners (org.springframework.boot)
accept:-1, 2130192211 (org.springframework.boot.SpringApplicationRunListeners$$Lambda$47)
forEach:1257, ArrayList (java.util)
doWithListeners:120, SpringApplicationRunListeners (org.springframework.boot)
doWithListeners:114, SpringApplicationRunListeners (org.springframework.boot)
environmentPrepared:65, SpringApplicationRunListeners (org.springframework.boot)
prepareEnvironment:344, SpringApplication (org.springframework.boot)
run:302, SpringApplication (org.springframework.boot)
run:1300, SpringApplication (org.springframework.boot)
run:1289, SpringApplication (org.springframework.boot)
main:11, PropertyEncryptBugDemoApplication (com.example.demo)
写这篇文章的此时,我又仔细debug了一阵,发现这块代码还是非常复杂,这一篇先收一收,先把核心答案讲一下,等后续再详细分析源码逻辑。
在正常情况下时(没有加入加密库),如下代码处,是可以正常执行的:
但是,在引入加密库后,加密库会修改propertySource的类型:
所以这里就会不一样,导致这个bootstrap.yml没有识别到,就会引起后续的一系列问题。
总结
在我找到问题答案后,拿着这块的关键字再去找,果然就发现了一点点资料了。
可以看这个issue,问题一模一样:
https://github.com/ulisesbocchio/jasypt-spring-boot/issues/296
但是没修复,被作者关闭了.
下面也是类似问题:
https://github.com/ulisesbocchio/jasypt-spring-boot/issues/289
解决办法:
最新版本也没修复,可以修改源码,或者先禁用这块功能:jasypt.encryptor.bootstrap=false
spring cloud与加密库jasypt(ulisesbocchio)冲突问题定位的更多相关文章
- 【Java库】如何使用优秀的加密库Jasypt来保护你的敏感信息?
1 简介 今天我们介绍一个Java库-Jasypt,全称为Java Simplified Encryption,用于加密解密.它能够让开发者用花费最小的工作而把加密集成到项目中,并且不需要对加密/解密 ...
- Spring Cloud开发人员如何解决服务冲突和实例乱窜?(IP实现方案)
一.背景 在我上一篇文章<Spring Cloud开发人员如何解决服务冲突和实例乱窜?>中提到使用服务的元数据来实现隔离和路由,有朋友问到能不能直接通过IP来实现?本文就和大家一起来讨论一 ...
- spring cloud config git库文件搜索顺序
spring.cloud.config.server.git.uri只配置到仓库那一层就行了,需要访问仓库的子目录的话就配置spring.cloud.config.server.git.searchP ...
- Spring Cloud开发人员如何解决服务冲突和实例乱窜?
一.背景 在我们开发微服务架构系统时,虽然说每个微服务都是孤立的可以单独开发,但实际上并非如此,要调试和测试你的服务不仅需要您的微服务启动和运行,还需要它的上下文服务.依赖的基础服务等都要运行:但如果 ...
- spring cloud 配置文件加密解密
1.底包 <dependency> <groupId>org.springframework.security</groupId> <artifact ...
- spring cloud 配置文件application.yml和bootstrap.yml 的定位,区别和联系
最近在启用springcloud配置中心server的东西,在整理属性资源的时候,突然发现:用了这么久的springboot,为什么会配置两个属性文件同时存在(application.yml/prop ...
- spring cloud 配置文件application.yml和bootstrap.yml 的定位,区别和联系总算是有一点明白了
最近在启用springcloud配置中心server的东西,在整理属性资源的时候,突然发现:用了这么久的springboot,为什么会配置两个属性文件同时存在(application.yml/prop ...
- 使用对称加密来加密Spring Cloud Config配置文件
补充 使用Spring Cloud Config加密功能需要下载JCE扩展,用于生成无限长度的密文.链接:http://www.oracle.com/technetwork/java/javase/d ...
- Spring Cloud 微服务架构全链路实践
阅读目录: 1. 网关请求流程 2. Eureka 服务治理 3. Config 配置中心 4. Hystrix 监控 5. 服务调用链路 6. ELK 日志链路 7. 统一格式返回 Java 微服务 ...
- 全链路实践Spring Cloud 微服务架构
Spring Cloud 微服务架构全链路实践Spring Cloud 微服务架构全链路实践 阅读目录: 网关请求流程 Eureka 服务治理 Config 配置中心 Hystrix 监控 服务调用链 ...
随机推荐
- java调用本机的命令 如ping、打开文本等
最近接触到用java代码调用主机的命令部分感觉有点意思整理总结一下 环境jdk1.8 操作系统win10,不用引入其他的包jdk自带的api就可以 一.java调用ping命令 import jav ...
- 【内核】深入分析内核panic(一)--内核问题的原因
1 概述 linux内核包括进程管理.内存管理.中断管理.设备驱动.同步机制等各种模块,它们共同运行在一个共享的地址空间中,因此在运行中一旦出现问题,彼此之间可能具有千丝万缕的联系. 而且与用户态不同 ...
- vscode prettier保存代码时自动格式化
https://blog.csdn.net/qq_37815596/article/details/109225879
- v-cloak指令用法
插值表达式存在的问题:'闪动' 如何解决该问题:使用v-cloak指令 解决该问题的原理:先隐藏,替换好值之后再显示最终的值 背后的原理:先通过样式隐藏内容,然后在内存中进行值得替换,替换好之后再显示 ...
- freeswitch上报信令到HOMER的配置方案
概述 HOMER是一款100%开源的针对SIP/VOIP/RTC的抓包工具和监控工具. 之前的文章中,我们介绍了HOMER的安装步骤,HOMER7的安装部署还是比较简单的,安装过程也比较顺利. 然后, ...
- ClickHouse中“大列”造成的JOIN的内存超限问题
ClickHouse中"大列"造成的JOIN的内存超限问题 "大列"是指单行数据量非常大的列,通常是100KiB以上.这样的列会导致JOIN(通常LEFT JO ...
- Vue事件方法中this.属性名
vue事件方法中访问data对象中的成员 : this.属性名 注意: 如果事件处理代码没有写到methods中,而是写在行内则不需要this.
- CSS 3D - rotate旋转90度看不到的原理 和 解决方法
原理: 旋转元素的坐标有三个 :X(向右), Y(向左) , Z(向电脑屏幕的你) 当没有位移旋转元素时,元素 Z 坐标也会同着一起旋转 ,当一个物品旋转到90度时,我们只能看到它的厚度,而d ...
- JMS微服务开发示例(九)相同的微服务,按用户所在城市来分配微服务器
虽然,默认情况下,多个相同的微服务,网关是自动根据微服务的压力情况,把用户请求分配到压力较轻的微服务器上. 但是,在某些业务情景下,我们可能希望人为去控制微服务的请求分配. 举个例子,我在北京.上海. ...
- [转帖]Oracle23c On linux的简单安装
Oracle23c On linux的简单安装 背景 Oracle11.2.0.4 发布之后 下一个版本是 Oracle12c 因为西方人比较不喜欢13这个数字, 尤其是犹太人出生的 拉里埃里森. 所 ...