文章首发公众号『风象南』
问题初现
某日下午,正在进行一个项目沟通会,团队A同学找到我
”昨天说的那个功能做好了,验证了下基本流程没问题,但是测试过程发现个问题,就是调用完我新加的那个方法后再次访问界面就失败了,服务端口、进程资源啥的都正常,我分析了下,暂时没找到问题,但是重启服务后再访问界面就没有问题。“
又说道:“新增的那个方法不是经常调用,也就配置初始化时候调一下,影响不大,这个问题要不要解决。”
我说:“那得解决呀,没找到问题原因你就不确定什么时候会再次发生,即使重启正常了,可能运行一会或者操作个啥就又出问题了,另外,万一这个问题影响的不止这一处不就是留了个大坑,你再去查查看,如果没解决我等会开完会一起看看“。
排查定位
然后我接着开会,随后又准备了一些项目材料、设计文档,忙完这些已经是晚上10:00了。
突然想起来中午A同学的那个问题,过去看了下A同学还在工位,我过去问了下
”找到问题了吗 ?“
A同学说:"还没有找到,他看了代码好像没什么问题,不知道是不是其他人提交的代码影响的。"
接着翻起了代码提交记录。
我说:“错误提示是什么,异常信息给我看下。”
A同学说:“服务端没有错误,就是前端界面访问提示错误。”
如下图
我看了下,有意思,我就喜欢这种一眼看上去错误提示很简单但没什么头绪的问题,关键错误就是这行
"ERR_SSL_PROTOCOL_ERROR"
,
大致表达的意思就是:错误的SSL协议
我问了下:"你新增的代码里有修改服务侧SSL的什么配置参数吗 ?"
A同学说:“没有。”
我说:“把SSL先关掉测试下。”
A同学关掉服务侧SSL,启动->调用他新增的方法->访问界面,没有问题
我说:“把SSL打开,在控制层注释调你新增的业务方法调用。”
A同学注释掉业务方法调用,启动->调用他新增的方法->访问界面,没有问题
我说:“这就排除了全局拦截、AOP之类的影响,那大概率就是你的方法里改了SSL的什么配置,导致前后端无法正常进行握手通信了。”
随后我同A同学又检查了一遍代码,确实没发现修改SSL相关配置的地方
我说:“Wireshake抓包看一下.”
A同学抓完包,分析后确实是握手过程失败了。奇了个怪。
过程回顾
随后,我开始思考整个过程。
1、关掉SSL没事,那一定是调用过程动了SSL的参数或者依赖的什么组件
2、开启SSL,注释调业务方法调用也没问题,说明全局拦截器、自定义的AOP逻辑里面也没有问题
3、抓包结果,前端提交的算法套件参数没有变化,服务端没有正常返回,说明肯定还是服务端的问题
4、那既然是服务端的问题,而且只发生在调用了新增的方法后,那问题肯定还是在整个新增的方法中
5、既然没有改SSL配置,有没有可能服务端的算法套件或者算法提供者被改变了,如果有使用过"Bouncy Castle" 这个库的同学应该知道,使用这个库需要添加一行代码
Security.addProvider(xxx)
而类似这种代码是可以修改Java运行中的密码算法提供者的,包括删除。
小样哪里走
我说:“加两个断点,1个在项目启动完看一下Provider列表,1个在调完新增的方法后在看一下Provider列表。"
A同学开始操作
项目启动完成通过通过调用方法Security.getProviders()
查看当前加载的Provider列表,可以看到列表中总共14个对象
调用新增的方法后,再次通过调用查看Provider列表的方法,此时,发现少了一个 SunEC。
那么大概率是因为SunEC被删除了导致的,大致确定了问题原因应该是删除了SunEC导致服务侧的算法提供者找不到了,无法正常提供SSL能力。当然,还需要验证。
而删除的Provider的关键代码就是
Security.removeProvider("SunEC")
随后,A同学进行代码全局搜索,没有发现这行代码的踪迹。
奇怪了,难道是引入的库里面删的 ?随后搜索范围加上了依赖的三方库。
果然,在一个公司封装的基础库的类里面找到了
而A同学刚好在他的逻辑过程中调用了该类的一个方法。
随后进行了验证,发现确实是这行代码带来的影响,然后将问题反馈到负责这个库的同事进行了调整。
至此,问题解决。
问题原因就是因为删除了JDK默认自带的SunEC Provider,由于HTTPS握手过程中服务侧依赖的算法提供者不能正常加载导致后续的算法套件、密码运算操作不能正常工作。
当然,仅通过文字还是无法完全呈现出问题分析定位的那种感觉与画面,珍惜每一次问题定位分析的过程,其中蕴含的思考、经验在未来都是你的知识资产与价值体现。
故事未完
看着A同学的背影
"一瞬间,又想起了曾经多少次为了解决一个问题,奋战到天亮的激情岁月。"
至今,激情未减,战斗仍在继续。。。