最近项目组运维团队的小伙伴提出想要对现有的阿波罗配置中心涉及到数据库等中间件相关数据的敏感信息进行加密处理的需求,由于数据库连接池采用的阿里的druid,而druid官方提供了RSA的非对称加密算法,参考过之后,发现能满足当前的需求,就直接参考官方的步骤进行改造,本以为大功告成,结果刚上线就出了篓子。
具体表现如下:由于相关服务都开发了阿波罗配置的动态监听功能@ApolloConfigChangeListener,通过applicationContext.publishEvent方法进行动态更新内存配置数据,当运维人员修改了公共配置,然后进行发布之后,过段时间会导致数据库账号锁定,这玩意只要动态发布,相关服务不重启,必现。
根据相关日志发现,每次动态发布之后,都会打印password changed的相关日志,源码如下:

为啥我改的无关的配置会导致数据库的密码变更,继续追踪发现通过applicationContext.publishEvent方法进行动态更新内存配置数据,分两种数据情况,一种是通过spring的@RefreshScope 修饰的类,另一种就是spring框架自带的配置类@ConfigurationProperties(prefix ="spring.datasource.druid") 类似druid的配置信息

当通过方法进行动态更新数据的时候,@RefreshScope 修饰的类类似于直接去掉缓存然后重新加载类实例塞入缓存来保证数据更新,而ConfigurationProperties是通过遍历配置类属性重新调用set方法塞入新值。由于配置中心塞入的是密文,而启动时加载到内存的password其实已经被解密为明文,当通过对比后,肯定是不相等的两个密码,所以会打印password changed的相关日志,那说明现在的加密方式是不行的,继续另想他法(apollo官方建议使用的jasypt加密也不能满足)。
数据库密码为啥会变,是因为通过apollo server推送过来的数据是密文,那如果推送过来的密码是明文密码,那这个问题就会迎刃而解,所以根据这个思路开始从apollo-client以及apollo-server入手寻找解决方案,鉴于修改apollo-server对所有服务都会产生影响,所以从各个服务依赖的apollo-client入手进行处理。
在springboot环境中,apollo-client的启动配置类为ApolloApplicationContextInitializer,启动初始会调用initialize(context)方法:


读取配置文件中的namespace,然后通过config.getConfig循环读取namespace的配置



初始化的时候调用updateConfig更新所有配置信息,还差一个动态发布时候的操作,简短点说其实就是在RemoteConfigRepository中存在一个RemoteConfigLongPollService remoteConfigLongPollService属性,初始化的时候每个remoteConfigRepository.onLongPollNotified用于接收apollo server端通知,在server端动态发布属性之后,调用如下方法:



最终还是调用updateConfig(newConfigProperties, sourceType);方法,所以最终的解决方案是在更新属性之前,将接收到的所有密文数据进行解密,然后继续原先的执行步骤即可。
上面是针对的特定需求整理的方案,可能还有不足之处,如有问题,希望不吝指出,多谢。
网友评论