记录今日一问题排查(认证失效或授权码错误问题)
今日得一BUG:在统一认证(shrio)的环境下,系统A调用系统B时出现失效/授权码错误的问题。
排查思路:
1.先看浏览器/后台报错信息。
可以看到错误的描述为:错误的授权码。
该信息由统一认证系统返回,根据此描述,可以在统一认证系统中全文搜索到报错代码出处。
进入此条件的途径为baseOauthService.checkAuthCode(authCode)==false
checkAuthCode方法为shrio包中的方法。反编译相关代码。
故大方向上,有两种可能,X:this.cache.get(authCode)==null Y:前端传过来的code是错乱的code
先开始X方向:
那么问题点就在cache。反编译,看看所有cache和code相关的操作
很明显,在登录成功的时候会addAuthCode,将code值,和username存入cache中。其中username来自SecurityUtils.getSubject().getPrincipal()。
那么将有3种情况会让chche去GET时返回null:
A. username==null.即SecurityUtils.getSubject().getPrincipal()==null,则本身存储时,存在cache里面的就是code,null
B.有地方调用了removeAuthcode方法,或者使用了put.(code,null)的操作将该code置空
C.cache中由于某种原因,该code,username的值没有了
从A情况来看。继续反编译getPrincipal()方法;
根据上面的代码可以看出,如果是Null,很可能是2种情况。session是null,所以返回null,导致最后返回null;或者runAsPrincipals的第一个元素是null。经排查getSession方法,发现session不会为null。那么可能是List第一个元素为Null。
很可能联想到一个常见问题,是否List在多线程高并发下,add失败,造成null值。
那么我们找到对应增加Attribute的地方。代码如下:
果然apache的项目应该不会犯这种错,shrio源代码使用了CopyOnWriteArrayList(常见的解决多线程下list并发安全问题的方法之一,增删改加锁,读不加锁,读写分离),那么也不存在线程安全问题。故排除。
B情况也搜了下,除了SSO注销会使用logout,会调用remove方法,其他没有途径主动Remove code
C情况太灵异,没有多想。
则思路转变到大思路中X / Y中的Y思路来,即前端由于某种原因,发生了错乱,传错了没有的code值。
首先code存于cookie中。要么cookie值没了,要么code错乱了。
如果是cookie值没了,可能是写cookie时的expire问题。查阅代码,发现后端使用了maxAge属性定义cookie时长(正数,负数,0,具体查阅相关HTTP协议),发现该属性是HTTP1.1新增,而HTTP1.0使用老的expire方法,一般来说会向下兼容,则expire在1.0和1.1应该都没有问题(expire有问题,依赖于客户端时间)。如果用maxAge,那么HTTP1.0的浏览器会不会有问题。但是经过沟通,发现出问题的机器用的浏览器,是HTTP1.1。那么这条路也断了。
不是cookie值没了,那么可能是code错乱了。很有可能是前端的某些操作会使session错乱。经查询,使用iframe,frameset等,会造成session错乱或者丢失的问题。项目中Ext.js用到iframe的部分和前端人员沟通,有报表。结果发现出问题的系统,经测试,报表操作不会引起该问题。此路也断。
重点来了,吃了几个面包以后,感觉思路变得清晰起来,想起来还有X思路中的C情况没有仔细考虑。
又回归到C中来。Cache中的code没了?
回归到管理session以及code的相关配置策略中来。session时长都是已经设置过的。那么可能是管理的相关策略。
使用了ehcache进行管理。
默认策略如下
具体的authcode如下
正常情况继承默认defaultCache,默认的时间为2分钟,但是如果又自己定义了具体名称的chache,则按自己定义的时间计算,例如code-cache。
具体相关配置含义可以查询相关文档。可以发现timeToIdleSeconds为36000秒,即600分钟。堆内存中最大缓存数对象为400。如果超过400会发生什么,可以看到默认配置策略中memoryStoreEvictionPolicy="LRU"(最近最少使用)。FIFO(先进先出),LFU(较少使用)。
那么很可能让code为null的情况是,超出了400个缓存数的code,则让最少用的那个code没了。并且子节点中配置 了overflowToDisk="false",则多出的缓存数并不会保存到磁盘。
又结合系统中对code的处理,我们发现只有从统一认证系统注销,才会清除code。而关闭浏览器其实并不会清除code,部分系统记住密码功能有问题的,关闭浏览器以后再登录系统,甚至还会再申请一个code,所以code会积累。
综上所述,如果600分钟内,各个系统的操作,除去主动注销的部分,600分钟内积累的缓存数超过400,则很可能少用的code将会丢失。所以也可以预计到,在高峰期出现失效的问题概率会较高,夜晚则很少。同时换个思路,可以用cache的属性去做一些限制登录控制流量的功能。
建议解决办法:增加最大缓存数,同时code时间最好也和各个系统同步。并且有关cookie相关的内容需要子系统统一考虑,例如使用localStorage,sessionStorgae。
所以任何问题的出现都是有原因的,通过一步步排查,总能找到问题的根源。
网友评论