近几日发现,我们维护的系统登录后不能正常退出了。点了“退出”按钮以后没反应,但不是每次都没反应。要想解决这个问题,就必须先弄清楚系统的架构。别以为就一个简单的登录退出,要是那么简单就不至于拿出来单练了。
这个问题其实涉及到了两个系统,一个是业务系统,另一个是采用SAML协议的SSO单点认证。在这里有必要简单说一下SSO的工作原理。客户访问业务系统,业务系统提供登录框,客户点登录按钮,请求被发送到SSO系统。发送过去的数据除了客户的用户名和密码外还包括客户在业务系统的session id(业务session)。由于客户也在SSO系统产生了session id(SSO session),则两个session id在SSO系统被绑定到一起。退出的时候,客户点击“退出”,请求直接发送到SSO系统。SSO根据当前客户的SSO session,识别出客户在业务系统的业务session,然后直接给业务系统发送注销请求,该请求会携带客户的业务session。说白了就是SSO替客户访问了一下业务系统的退出接口。当然,业务系统可能不止一个,SSO会根据需要往每一个业务系统发送注销请求。这就是业界普遍采用的SSO方案。这种方式可直接跨域,客户对SSO系统无感知,是一种很好的方案。
业务系统由多台机器通过F5做负载均衡。相同的客户只要session不改变,会始终访问到同一台机器。
出现问题的页面是www.abc.com上的一个页面。该页面通过iframe嵌套了www.abc.com/sp1的一个页面login.jsp。login.jsp就是问题中的登录页面。
架构基本就是这样,下面开始解决问题。
首先这个问题在IE和火狐上都会出现,说明一定不是IE6的所谓的“问题”。
客户能够正常登录,说明SSO基本工作正常。登录后不能退出,则有多种可能性。首先想到的就是SSO不能正常发送注销请求到业务系统。但有的时候是可以退出的,这说明SSO有时候是可以正常发送的。况且,此问题是最近才出现的,SSO系统在最近一段时间未做过任何改动。所以,暂时假设SSO系统工作正常。
那么,是不是SSO系统在给各个机器发送注销请求的时候错乱了呢?比如客户在业务系统的机器1上登录的,SSO却把注销请求发送到了机器2上?仔细想想,这也不可能。因为SSO的当前配置是把注销请求发送到所有机器上。难道是网络故障?在排除所有软件故障之前,我一般都假定网络是正常的。这就好比是在全球变暖后,我们应该先从生活其上的人类身上找原因,而不应该先找地球自身的问题。
如果以上的疑点都被排除,那么剩下的就是:SSO并没有把客户的SSO session和业务session正常匹配到一起。
session id是哪里来的呢?是通过前台发给SSO的。为了把复杂问题简单化,我不通过www.abc.com访问被嵌套的login.jsp,而是直接访问login.jsp。这时,不管我尝试多少次,都可以正常的登录和退出。此时,我觉得真想已经离我很近了。问题一定就在www.abc.com上。
我打开IE6和httpwatch,仔细检查登录的每一个步骤。在登录的请求当中,我发现发送给SSO的业务session id(参数名为sid)和cookie中的session id不一致!真是岂有此理。login.jsp加载的时候,会在服务器端生成sid。而登录请求是在页面都加在完毕后,由客户触发的。也就是说,客户在www.abc.com产生的session id在中途被修改了。
cookie中第一次出现session id,是在访问了login.jsp之后,之前都是静态页面。session id一直都没有变化,直到访问到www.abc.com/sp2的一个页面。sp1和sp2是两个不同的工程,都部署在业务系统的weblogic上。sp2也是通过iframe嵌入到www.abc.com。从session id变化之后加载的sp1上的页面都开始使用新的session id了。当页面全部加在完成后,session id已经被改过一次了,所以登录的时候sid和cookie中的session id已经不一致了。
经过询问得知,sp2的上线时间和SSO开始出现故障的时间基本吻合,所以我断定这就是问题所在了。经过多次测试我发现,iframe的加载顺序是不定的。如果login.jsp在sp2之前加载,基本上就不能退出了,反之则可以退出。
最终结论就是,不同的iframe会在同一个页面产生不同的session id,并且后加载的iframe会覆盖先加载的iframe的session id。开始我还以为是由于F5的负载均衡造成的,后来拿单机做测试,也还是有同样的问题。找到的资料说,在被嵌页面的header中加上P3P: CP=CAO PSA OUR就能解决这个问题,但我的实验并不成功。
知道了原因就好解决问题了。我让sid这个参数在登录的时刻通过JS从cookie中获取,这样就肯定没问题了。
之所以写这么多,就是为了加强自己分析问题和解决问题的能力。问题本身并不重要,过程才是最重要的。
这个问题的本质就是iframe和主页cookie不同步的问题... 同样的问题在flash上也存在..当然,不用说, 肯定ActiveX, Java Applet也是存在这个问题的.
解决的问题很简单. 通过URL传递就行了..<iframe src="page.jsp?sessid=qe329dk259fl29ad56u765ge432">
=然后page.jsp从URL上获取sessid
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。