原文地址: http://www.cnblogs.com/AloneSword/p/3840548.htmljavascript
最近在使用sina微博时,常常性交替使用 weibo.com 和 t.sina.cm.cn进入个人微博。发现当我在 t.sina.com.cn中登陆以后,直接切换至weibo.com,这时候在 weibo.com是已经登陆的,当我在 weibo.com进行注销以后,再切换至 t.sina.com.cn,这时候在 t.sina.com.cn也已是注销的状态了。
对于SSO的实现方案及其机制,早已经不是什么新鲜的技术了,从微软为.net提供的passport机制到java中开源的JBoss SSO、Oracle OpenSSO及经典的 Yale CAS等等之类的开源或一些商业SSO中间件都不失为做为单点登陆实现的选择。固然一些企业也会选择本身实现一套适合本身轻量级方案,如采用 SESSIONID转递或SESSION同步复制之类的。 能够看得出SSO的价值也是具大的,就拿sina来讲吧,增长 weibo.com域名以后,对于用户来讲来讲没有任何影响,即便你在 t.sina.com.cn中进行登陆,能够无缝在两域名之间随意切换,对于它推广weibo.com无非是大大的益处。
因为近年来一直在使用 Yale的CAS做为SSO的方案,以为 SINA的SSO与Yale-CAS有不少殊途同归之妙,因而便对SINA的SSO进行分析,其中的细节处理仍是很值的学习的。固然,因为分析看到的 SINA SSO处理都只是一些表现或表面上的东西,再加上其大部分关键的sso js都已经被压缩,及SERVER端的实现机制也只是靠本身的经验及结合CAS的的一些原理进行猜想。其实本文应该叫 更比较贴切。
好吧,进入正题。
php
首先是进入 t.sina.com.cn,提交用户名及密码进行登陆,经过 Firebug能够看到它经过相似Aajx POST到了 http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.3.12),以下图所 示:
不难看出,其 http://login.sina.com.cn/sso/login.php 就是相似是 CAS 中的 Server,对sina的全部应用系统提供的统一登陆入口。上面的参数中有一个service参数,了解 CAS的GG应该知道 cas 在登陆的时候除了username 和 password一样也有一个 service 参数,其CAS该参数含义是子应用系统的服务名标识及登陆成功以后所跳转的地址。固然,sina这里使用了 "miniblog"做为微博的服务名,估计他在sso-server端对 miniblog 与登陆成功以后的地址进行映射,如 miniblog=http://t.sina.com.cn/,这样就避免了CAS-client中转入service= decodeURIComponent('http://t.sina.com.cn')之类的作法了。
这里的登陆与CAS作法一致,将登陆验证提交至统一的认证中心进行验证处理,从而避免了跨子域和全域的问题。 验证成功以后路转的路径就是service 所向的地址,验证失败以后则返回至当前登陆页。下面就SSO中的一些登陆方面的核心问题作一些分析,看看SINA和CAS分别是如何处理的:
1.如何受权某个子系统容许其在sso-server进行登陆验证呢,相似cas-server中的login-ticket;
对于cas来讲,在首次进入 /cas/login页时, 会产生一个一次性的login-ticket,也就是说在提交登陆验证前必须向服务器请求一个login-ticket,在登陆提交时,须要将用户名及密码以及login-ticket进行提交至 cas-server端,cas-server端肯定login-ticket有效后才会对用户名及密码进行认证。
看看sina如何处理的吧,继续看firebug:
以上截图是当我首次进行 t.sina.com.cn时,经过 ajax/jsonp的方式发起的一个请求,能够看到返回的callback函数中的 json 串中包含了 nonce:"SXK19N"的属性,参数名的汉译是“一次”或“一次性”的意思,估计这里的 nonce就是login-ticket,为再一次确实,我再试着提交登陆看看,看它是否将该参数POST过去:
果真不出所料, nonce:"SXK19N"做为参数提交过去了,证实所猜想的应该是正确的。
2.好比验证码跨域跨服务器致使从session没法获取的问题,咱们曾经遇到过;
貌似sina登陆没有涉及到验证码之类的东西,当你屡次登陆失败以后,它采用的是“您的登陆过于频繁,请稍后再试吧”,这种方案确实比验证码要好的多,并且还避免了上面的说的问题。
3. 当我登陆失败了,/sso/login.php 如何将登陆的错误信息返回给 t.sina.com.cn并让它进行显示呢,若是我登陆成功了/sso/login.php 经过什么方式通知t.sina.com.cn呢,由于它这里使用的是ajax方式登陆?
对于这方面,cas的处理是将错误信息以参数的方式返回给 client-login,如登陆失败,重定向地址: http://cas-client.com?errocode=0,若是登陆成功,则直接 重定向至 service 中的url,并生成ST给客户端,表示其已经在cas-server登陆成功了。
看看sina如何处理的吧,随便输入一个用户名密码,提交登陆,继续经过firebug看看它的处理过程:
再看看t.sina.com.cn 中的html内容的变化:
以上图1中发生了两次请求,第一次登陆验证是访问 sso认证中心,它所返回response是一个html内容,第二次请求的地址: http://t.sina.com.cn /ajaxlogin.php framelogin=1& callback=parent.sinaSSOController.feedBackUrlCallBack&retcode=4038& amp;reason=���)
再结合以上图2信息,看到 html 中发生了变化,建立了一个 id=ssoLoginFrame 的iframe,因而即可以得出,sina 的登陆并不是原生的ajax方式,而是经过建立iframe来模拟提交不刷新的登陆。也就是说,当用户点击登陆提交时,这时候它会经过js建立 iframe,将登陆提效至该iframe中。
既然已经知道它登陆是提交到iframe中,而非ajax方式,那么对于以上截图1中两个请求为何返回的都是HTML内容就很容易解释了。再回到上面的 问题,/sso/login是如何通知t.sina.com.cn登陆失败了呢? 首先在以上第一个截图中返回的 HTML包含了一段 javascript:
html
location.replace的意思与location.href相似,一样都是改变当前的URL地址,具体区别及作法能够参考这里及这里。须要注意的这里所说的经过location.replace改变当前的URL其它并不是改变t.sina.com.cn的地址,而是第二个截图里iframe中src的地址,由于这段HTML是在iframe中输出的。
在 locaiton.replace 的地址中包含了一个 retcode 及 reason参数,估计这就是当前登陆的错误信息。在上面第一个截图的第二个请求实际就是在iframe 中进行的 location.replace操做后的跳转地址。关键看它输出的html内容:
java
国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html
内部邀请码:C8E245J (不写邀请码,没有现金送)
国内私募机构九鼎控股打造,九鼎投资是在全国股份转让系统挂牌的公众公司,股票代码为430719,为“中国PE第一股”,市值超1000亿元。 jquery
------------------------------------------------------------------------------------------------------------------------------------------------------------------web
这段js是在 iframe中执行的,因此能够经过 parent 进行访问 t.sina.com.cn中的js,能够确定 parent.sinaSSOController.feedBackUrlCallBack 就是告诉 t.sina.com.cn 当前已经登陆失败了,而且将错误信息传至该入该callback了。至此,已经完成了 /sso/login.php 对 t.sina.com.cn的信息传送。 新浪果真是有一手呀,在CAS中AJAX登陆一直都是一个问题,而sina它巧妙的经过 iframe+callback 进行实现了。
接着,再看看它对于登陆成功以后如何通知 t.sina.com.cn的吧,先看看登陆成功以后 sina-sso-server 会作什么,看firebug截图:
重点在于 set-Cookie: tgc=TGT-MTc4NTc0NzM0Mw==-1305003116-ja- D51B2EB107B79FC50D8CA424BFE08907; 哈哈,熟悉CAS的应该会很熟悉这个,没想到SINA的TGT与CAS的TGT不但参数命名,竟然连生成的规则也如出一辙,估计sina确定是参考了 cas 的实现机制。关于TGT是什么或其做用能够参考:CAS总结之Ticket篇。另外还有一个就是当登陆成功以后,sina-sso-server会将用户登录名等等放在sina.com.cn根域的cookie中。
而后再看看登陆成功以后 sina-sso-server所返回的response内容:
如下是从以上摘取JS部分:
ajax
首先再次声明,以上firebug截图中的请求处理,并不是 AJAX,而是在 t.sina.com.cn中放了一个iframe,输出的 reponse都会至iframe当中.
以上的js主要重点在于:
json
仍是经过设置当前iframe中src地址,再看看跳转至http://t.sina.com.cn/ajaxlogin.php后的response内容吧:
返回用户信息(从cookie中获取的),而且仍是相似上面的作法,经过 parent.sinaSSOController.feedBackUrlCallBack回调t.sina.com.cn中的js,告诉它这个用户已经登陆成功了。
因而t.sina.com.cn便进行跳转至 t.sina.com.cn/dengers 中,从而实现登陆。
总体的处理流程以下:
4. 当我在t.sina.com.cn中登陆后,切换至weibo.com,weibo.com我应该也是已经登陆的,如何作到呢?
对于这个问题,CAS中的处理就是,当我进入 weibo.com的时候,立刻跳转至 /cas/login,而后在login中判断cookie是否存在TGT,若是存在,并肯定其有效性后,则认为你已经登陆,并为你生成一个ST,将ST 做为ticket参数使其重定向至 weibo.com?ticket=TG-xxxx 并登陆。
看看sina怎么处理的吧,首先我直接在t.sina.com.cn登陆成功。而后再新建一个选项卡,输入 weibo.com:
能够看得出,当我进入 weibo.com以后,sina并无直接进入 weibo.com的主页,而是立刻重定向至: http://login.sina.com.cn/sso/login.php?url=http://weibo.com/&_rand=1305008634.5127&gateway=1&service=miniblog&useticket=1&returntype=META 与cas的作法确实一致。 再看看该 login.php的Response 信息,主要是JS:
api
看到这里以后,不得不怀疑 SINA 的 SSO 是否是用的就是 CAS 啊!!不但连 TGT 参数名同样,连ST规则及参数名也如出一辙,其处理机制也十分类似。
到这里以后就与 CAS 的处理同样了,就不详细写了,能够参考 CAS相关文章。
──────────
PS:因为在分析过程当中里面的不少SSO关键JS都压缩了,因此不免会存在偏差。 不过SINA的SSO不少细节方面确实处理的很好,做为互联网 应用的话,若是单纯的只是把 CAS DOWNLOAD 下来,而后直接配配就用的话不少方面的处理仍是很不到位的。 有时间我把咱们CAS参考 SINA 调整一下。
到这里,不得不说的一个事情就是,以前在分析淘宝cookie如何跨域获取时, 你们都说出了一个taobao的jsonp实际存在必定的安全隐患。后面那个淘宝的GG看到以后加入Refer的判断。而如今,在分析的过程当中发现新浪也 有这样的问题,能够尝试一下,随便在本地创建一个html,引入jquery,而后使用下面的JS,就能够获取到sina中的登陆邮箱名等信息,前提是你 须要先在sina中登陆:
跨域