K8s部署方式中shiro登录部分出现认证不通过
现象:
首页登录之后,刷新页面过程中部分接口出现shiro报错401,报错内容为 is not authenticate,导致接口无法正常查询数据
分析过程:
1、首先观察到出现问题的接口上都有一个shiro框架的注解@RequiresAuthentication,查看shiro源码,发现该注解会对登录访问请求做是否登录验证,进一步分析该登录状态记录在shiro中的subject对象中,
该对象保存在每次访问的线程threadLocal中,通过shiro源码发现,系统中的shiro使用了redis作为session共享的存储器,会登录状态以session形式保存在redis中,并将cookie返回给前端,以便下次访问带回。
2、由于在上云过程中,使用了云供应商的redis,所以将分析点转向了redis。首先是咨询azure云供应商,是否出现过该类问题,然后让他们将paas的redis设置成单机模式,再次尝试,均无果。
3、将redis替换成云下原有的redis,无果。将redis恢复成上云之前的sentinel哨兵集群模式,无果
4、因为上云过程中,做了容器化改造,怀疑是jdk版本问题导致,发现容器中的redis是openjdk,开始将基础容器镜像的jdk更换为正式版jdk,设置tomcat的jdk引用,更改之后,发现仍然无果。
5、上云过程中的另一个改造就是,原本的多机tomcat部署,改成了k8s的多pod部署。将怀疑点转移到了k8s上,想到一点k8s的后端部署负载均衡不在依赖nginx,同一IP在访问过程中可能会出现落在不同pod的情况,相对比于nginx中的stick属性,也许会导致session失效。首先将pod数量调整为1,也就是单机模式发现问题消失。接下来查阅k8s官方文档,发现在service属性中存在一个字段sessionAffinity,这个字段设置成ClientIP后,能实现客户端同一ip只访问一个pod的规则。修改尝试,奇迹出现,问题消失了。
最终分析:
shiro在共享session的解决方案中使用了redis作为存储介质,但是怀疑shiro任然自己保留有缓存,所以同一IP到了不同pod上,会导致session失效