实际应用:Sohu的Passport将 focus.cn,17173.com,sogou.com,chinaren.com这四个域名下的产品全部整合在一起了。用户在这四个站点中任何一个 地方都可以登录。当用户登录后可以自由的使用其他域名下的服务。现在很多网站上都有bbs blog album服务。这些服务一般也是自己维护自己的用户信息。当发展到一定时候,也需要一个Passport机制整合所有服务,使用户可以单点登录。
Sohu的实现方案
在http://passport.sohu.com/ 登录后 fiddler可以拦截到如下的返回信息:
![](https://i-blog.csdnimg.cn/blog_migrate/3bd1c07c3b0970df0ba4b089106286f0.jpeg)
由 于passport.sohu.com的登录界面使用了iframe隐藏提交。所以页面没有看到刷新。隐藏的iframe把用户名和加密的 password和其他信息发送给了passport.sohu.com。passport.sohu.com在Response中设置了成功登录的 cookie。这个cookie可以证实这个用户成功登录了passport.sohu.com。
![](https://i-blog.csdnimg.cn/blog_migrate/1d785d0298643017e4db2839962b8c03.jpeg)
当用户在Passport成功登录后。客户端的Javascript根据成功登录的标志,操作iframe请求http://passport.sohu.com/sso/crossdomain_all.jsp?action=login 因为在同一个域名下,没有跨域,在这次请求中,上次成功登陆的cookie会被一并带着回去。服务器端检查到成功登录的cookie后会Render回一段同时登录多个站点的html。
![](https://i-blog.csdnimg.cn/blog_migrate/3a05a6fa5d15fc1ba436fd5a7a1a4999.jpeg)
这 段html 要向4个地址发送请求。截至到现在都是在相同的Domain(passport.sohu.com)请求和返回,为真正的跨站点登录做准备,真正的跨站点 登录还没有开始。下面passport.sohu.com通过sso/crossdomain.jsp 在服务器端进行Redirect 设置http head 为302进行跳转。跳转后在这个跳转后的域名下设置登录成功的cookie。这就是sohu实现跨站点登录的核心过程。下面是 passport.sohu.com登录17173.com的过程。
1. 通过http://passport.sohu.com/sso/crossdomain_all.jsp?action=login Render回来的script <script type="text/javascript" src="http://passport.sohu.com/sso/crossdomain.jsp?action=login&domain=17173.com"></script> 请求同域下的http://passport.sohu.com/sso/crossdomain.jsp?action=login&domain=17173.com 这时passport.sohu.com下成功登录的cookie会被带回去。
![](https://i-blog.csdnimg.cn/blog_migrate/59cfd3b13b93b512ee0b24f2b0f0bea7.jpeg)
2. 服务器看到成功登录的Cookie后。在服务器端计算出一个加密后的17173.com的登录Url,并Redirect到这个Url。
![](https://i-blog.csdnimg.cn/blog_migrate/a4876c5cc9d32c255e88ff4c93b87f81.jpeg)
3. 17173.com 从url的QueryString中取得信息。并在Response中设置Cookie。这个Cookie终于写到了17173.com下。而不是 passport.sohu.com下。从而使得用户在17173.com下登录。其实用户在17173.com下手动登录也是写上同样的Cookie。 以后用户再访问17173.com的页面时这个Cookie会被带回去。这就表示用户在17173.com下成功登录过了。
![](https://i-blog.csdnimg.cn/blog_migrate/ddd8044f0b08ec6ff5d44ad4f0cb59bb.jpeg)
经过上面的步骤。用户在passport.sohu.com下登录的同时也在其他站点登录了。
在上面的过程中,最核心的技巧就是在指定的域下写入想要的Cookie:
1. Sohu 使用了在同一个域名登录后通过再次请求这个域名下某个链接后,得到要登录站点的请求Url,通过javascript使隐藏的iframe请求要登录站点 的Url,服务器端接到请求Redirect到要登录站点,然后通过Response写入Cookie,完成跨域名写Cookie的操作。这种写 Cookie的方式,需要在跳转时对请求的QueryString进行加密。接受方需要对QueryString进行解密。
2. 这 种做法在服务器端不需要特别的处理。只要写好相应Post操作 WriteCookie操作 Redirect操作 就可以了。在FireFox下就可以正常工作了。但是在IE下写Cookie的操作还不行,总是写不进去Cookie。需要在Response中加入一段 特别的Header. P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"
这个Http Header 是P3P安全的要求。P3P的详解 http://www.oreilly.com.cn/book.php?bn=7-302-07170-5
微软对这个的解释:
一个更加轻量级的方案
Sohu 的通行证方案已经可以轻松的将各个域名下的用户都同步登录了。但是在实现上Sohu会让客户端的浏览器请求两次passport.sohu.com。在第 二次得到一个登录多个站点的地址列表。在第三次请求时通过本域下的cookie进行身份验证,最后在服务器端跳转到一个含有加密Key的其它域名的Url 地址,最终写入登录成功的Cookie。除去最开始的登录,要求用户在登录后再进行两次请求。并且服务器端要再做一次跳转。Sohu的做法可能由于 Sohu服务器环境和数据存储的结构所决定。
其实总共只需一次登录请求,和每个域名下一次请求就可以完成多站点登录了,同时也不需要服务器端的跳转。
![](https://i-blog.csdnimg.cn/blog_migrate/ac466e03db45e519c3b7f775d631c1dd.jpeg)
跨站点的请求由script的src发出。各个域名下的ssologin处理QueryString中的key,解密key,验证Key的合法性。在Response中写入登录成功的Cookie。完成跨站点Cookie的写入。
两种方案的比较
1. Sohu使用的登录方式,请求次数多,但是每次请求都有对应的验证过程,在服务端跳转时,重要的跳转Url地址在HttpHeader中,使得跳转地址更加安全,使得用户在跨域登录时非常安全可靠。
2. 轻量级方案的登录方式,请求次数少,没有服务器端的跳转,对服务器压力小。但是需要对Key进行加密解密。跳转的Url会在Response的Http Body中Render给用户。在使用轻量级方案的时候,最好在Key中加上时间戳,过期时间设置为3分钟。Key过期认为这个Key是非法Key,不在 Response中写入登录成功的Cookie。
--------------------------CS架构-----------------------------------------
cs:
用户在这些系统中的用户名,密码各不相同,如:员工号为001的员工在这些系统中的用户名,密码分别如下:
用户 | 系统 | 用户名 | 密码 |
---|---|---|---|
001 | Portal系统 | A | 1234 |
001 | 邮件系统 | B | 2345 |
001 | DOMINO系统 | C | AAAA |
001 | 报销系统 | D | CCCC |
001 | 工资系统 | E | BBBB |
首先,建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系
首先,要建立员工在PORTAL系统中的用户名和其他系统中的用户名之间的对应关系并保存。可保存在表中或LDAP中或文件系统中。当然要考虑 这些系统之间的数据同步问题。比较好的方式是找到用户在这些系统中的都存在的唯一信息(如员工号,MAIL地址,姓名等)。通过唯一信息实时到各个系统中 去取认证所需要的信息。就不需要考虑数据同步问题。比较实用。可以建立类似下面的表:密码可采用加密保存。如果是采用BEA的Weblogic Portal,可采用UUP来保存这些信息。
( user varchar2(20), /*用户名*/ app_name varchar2(20), /*应用系统*/ architect varchar2(4), /*应用系统的架构BS或CS*/ app_company varchar2(50), /*用户所属分公司*/ app_department varchar2(50), /*用户所在的部门*/ app_user varchar2(15), /*在该系统中的用户名*/ app_passwd varchar2(15), /*在该系统中的密码*/ app_cookie varchar2(30), /*COOKIE名称*/ form_user varchar2(20), /*认证页面中FORM的用户名字段*/ form_passwd varchar2(20), /*认证页面中FORM的密码字段*/ app_special varchar2(20) /*其他*/ );
本人的建议是对这种系统不要自己去实现SSO。很麻烦,其实输个用户名,密码没什么大不了的。如果要实现,一是采用商业软件。另外也可以采用以下方 式:在PORTAL的PORTLET上建立超连接。并通过APPLET方式启动CS结构的应用系统的登录界面。然后通过如下的方式把用户名/密码传递过 去。
-不能做任何改动的客户端 -
WIN消息(给登录窗口发送用户名,密码等登录所需要的信息),模拟键盘(java有模拟键盘输入的API)
-可以做改动的客户端 - 参数传递,并让登录的EXE文件读取参数进行认证。
--------------------------------------------------------------------------------
前两天看到一个帖子《多站点整合—单点登录简单方案》,一直没顾得上回复。我想,Sohu并不仅仅为了“需要设计一个统一的登录界面”而考虑,它在安全性、授权机制上下了很大功夫。在这里对作者Zesson的分析做一些补充。
需 要统一登录界面是SSO(Single Sign On)的一个典型场景,此外相关的还有认证,同步,授权与统一登出等等,最为复杂的往往是同步,授权这两个环节。SSO直译过来叫单点登录,是对 Portal这个继续流行的企业业务整合方案的另一种称谓。由于整合范围和技术不同,主要有Web层单点登录、跨越Web层单点登录(Web与传统系 统),传统系统单点登录(Portal)等等。
记得2004年在做某单位的Protal系统时,因为要整合很多来自不同编程语言 (PB,VB,Dephi),不同编程时期的程序,遇到很多极为棘手的问题,调整方案往往对现有系统造成伤筋动骨的伤害,或者根本就是重做,最后系统也不 了了之。而现在在Web层实现单点登录相较就简单了很多,比如在我们建立的Portal页面上有每个网站的链接,用户点击这些链接就可以了。
J,当然还有更好的形式,我们先回顾一下基础知识。
因 为HTTP 协议是无状态的,我们想在Web开发中维持服务器段的应用状态可以利用Session。而基于Web的身份验证实质是用户在表单中输入用户名和密码后提 交,服务器从Request中提取用户名和密码,通过第一次验证后用Session来存储客户的登录信息。以后每次Request过来时,便从 Session中读取这些信息,如果有则进入相应界面,没有则认为未登录。
而Cookie特性则帮助我们在客户端维持状态。服务器在 HTTP的Response头上加上一行以提示浏览器按照指示生成相应的Cookie。Cookie主要有名字,值,过期时间,路经和域这些字段。浏览器 会按照一定的时间原则在后台自动发送给服务器,如果Cookie声明的作用范围大于等于将要请求的资源所在位置,则把该Cookie附在Request头 上。
如果设置了Cookie的有效期,浏览器就会把Cookie保存到硬盘上,这个Cookie可以在不同的浏览器中实现共享。如果不设 置有效期或者设置为负数,这个Cookie将保存在内存中。对于内存中的Cookie,IE与同一IE新建窗口的进程可以共享,而与其它方式打开的(如 “我的电脑”)IE进程则不能共享;JavaScript的window.open打开的窗口可以与原窗口共享;FireFox的进程或标签页可以共享。 关闭浏览器,内存中的Cookie就终止了。
服务器端为每一个Session编号,以便提取与某个具体的交互过程相关的Session, 同时也需要在客户端保存编号以区分具体的交互过程,这样便可以利用Cookie。 如果浏览器设置为禁用Cookie,服务器可以利用Request的提交来维持Session在客户端的标志。在地址栏中看到类似于 JSessionID=xxxxxxxx的就是Get方式,服务器在返回请求时总是将与该交互过程相关的SessionId追加到URL上,以保持状态。 也可以在返回的HTML的Form表单中追加hidden,将Session标志放进去,Form提交往往是Post方式。浏览器关闭时并不会通知服务 器,如果Cookie保存在硬盘上,那么关掉浏览器再打开还会继续前面的Session。因此服务器端需要对Session设置一个期限,多长时间未收到 客户端的请求将让Seesion实效。
综上,如果浏览器登录过某Web系统,服务器端生成了Session,客户端的Cookie中记录 了SessionId,浏览器没有关闭,即使使用内存中的Cookie,访问其它Web系统再回来只要Session没有失效,服务器还是会认为登录是有 效的。因此利用Cookie便可以达到SSO。浏览器登录Web A的页面后生成一个Cookie,再访问Web B的页面时通过访问服务器端通过Cookie就可以判断该客户段是否已成功登录。基于这样思路的就是SSO的Agent方式。
目前SSO有两种主流实现方案,Agent和P2222roxy。如果是非跨域的情况,还有共享Session的方式,如WebLogic和金蝶。
Agent方式是,增加一台SSO认证服务器,然后在每一个Web应用系统上安装一个Agent,由它访问SSO认证服务器来实现统一的身份验证和访问控制。SSO认证服务器存储了各Web应用系统的用户之间的映射方式。这样的缺点是必须改造现有系统。
一次SSO过程大致为:
1. 用户第一次访问Web A的页面
2. Web A发现用户未登录
3. Web A 调用AgentA
4. AgentA将页面转向SSO Auth
5. SSO Auth 返回登录页面
6. 用户输入用户名密码提交
7. SSO Auth验证密码成功
8. SSO Auth调用AgentA的URL传递帐号信息
9. AgentA解密帐号信息,传送给Web A
10. Web A进行授权,返回原来请求的页面
11. 用户访问Web B
12. Web B发现用户未登录,调用AgentB
13. AgentB将页面转向SSO Auth
14. SSO Auth发现用户已经登录
15. SSO Auth调用AgentB的URL传递帐号信息
16. AgentB解密帐号信息,传送给Web B
17. Web B进行授权,返回原来请求的页面
由于第5步与第14步都是在同一域下进行的,因此可以直接访问浏览器中的Cookie,以验明正身,不存在跨域取Cookie的问题。
看了Zesson的分析数据,我认为:
1. Sohu使用的是Agent方式,但是对它有一些改动,不需要独立安装,在现有系统扩充一些代码就可以。
2. 它的认证服务器映射了用户信息但不做验证,登录的验证由每个网站自己维护,在登录成功后通知其他网站(登录成功后在页面上生成每个Web系统的Token URL作为ticket)。
3. 出于安全考虑,URL中只存放Token,不会存放用户相关的信息。
Sohu SSO可能的过程如下:
1. 用户登录Sohu的Passport的login,提交用户名密码
2. Sohu login验证登录成功后返回,生成脚本http://passport.sohu.com/sso/crossdomain_all.jsp?action=login,刷iframe以来准备调用Agent
3. Crossdomain_all回写/sso/crossdomain.jsp?action=login&domain=17173.com, 页面刷iframe调用Agent
4. Sohu Agent将登录信息传递给 SSO
5. SSO 生成Token或者包含Token的URL,返回Sohu Agent
6. Sohu Agent 将调用其它网站Agent的URL传给页面,如pass.17173.com/sso/setcookie.jsp?passport= xxxxxxxxxxxx
7. 页面刷iframe,调用17173的Agent
8. 17173 Agent将Token(就是那个passport)传递给SSO获取验证
9. SSO返回验证成功及17173用户信息
10. 17173 Agent调用17173登录模块进行授权
11. 17173登录模块在响应中设置Cookie
在这里认为Sohu有统一认证的证据是,调用其它网站时所用的passport是一致的。
由于前期开发时是各自为政(如Sohu和Chinaren),系统都有各自的认证和授权,后来合并后提取统一的方式比较困难,便使用了现在这种既灵活,侵入性也不大的方案。
补充一下
1 FireFox的新标签或新窗口与原来的窗口使用同一进程,所以Cookie可以共享。
2 IE的Ctrl+N也还是与以前的窗口使用同一进程,所以Cookie可以共享。双击新打开的IE使用新的进程,就无法共享了。
3 在Explorer(如我的电脑中)输入网址,并没有使用IE进程,还是Explorer的进程,因此在别的Explorer(在此窗口中Ctrl+N, 或者双击我的电脑)中可以保持共享。JavaScript的window.open并没有开启新进程,但是不可以共享。