Passport 一方面意味著用一個(gè)帳號(hào)可以在不同服務(wù)里登錄,另一方面就是在一個(gè)服務(wù)里面登錄后可以無(wú)障礙的漫游到其他服務(wù)里面去。坦白說(shuō),目前 sohu passport 在這一點(diǎn)實(shí)現(xiàn)的很爛(不過(guò)俺的工作就是要把它做好啦,hehe)
搜狐的 SSO 需求比較麻煩,因?yàn)樗煜掠泻枚嘤蛎簊ohu.com、chinaren.com、sogou.com、focus.cn、17173.com、go2map.com,登錄用戶(hù)漫游的主要障礙也來(lái)自于此。
以前億郵的郵件系統(tǒng)在和別的系統(tǒng)整合的時(shí)候是提供一個(gè) URL,用戶(hù)從第三方系統(tǒng)里面點(diǎn)擊這個(gè)鏈接就可以生成訪(fǎng)問(wèn)郵件界面所需的 cookie,然后進(jìn)入郵件。這個(gè)方式的確很有效,但問(wèn)題是:
1. 每個(gè)外部鏈接都必須用特殊的 URL 跳轉(zhuǎn),維護(hù)很麻煩
2. 兩個(gè)系統(tǒng)集成已經(jīng)很麻煩了,若是集成的系統(tǒng)有好幾個(gè),彼此都需要跳轉(zhuǎn)而缺乏一個(gè)中心機(jī)制就成了噩夢(mèng)
3. 根本無(wú)法處理用戶(hù)直接在地址欄輸入地址進(jìn)行訪(fǎng)問(wèn)的情況
即使是跨域,上述的解決方法相對(duì)來(lái)說(shuō)還是容易的。
A. 首先是所有登錄必須首先通過(guò)一個(gè)中央服務(wù)器進(jìn)行認(rèn)證,然后在它那里給瀏覽器種下 cookie(下面稱(chēng)之為 sso cookie)
B. 當(dāng)用戶(hù)訪(fǎng)問(wèn)另外的域名 app 的時(shí)候,瀏覽器是無(wú)法直接發(fā)送 sso cookie 給服務(wù)器認(rèn)證的。此時(shí)應(yīng)該利用 javascript,動(dòng)態(tài)創(chuàng)建一個(gè)隱藏的 iframe,讓其訪(fǎng)問(wèn) sso
C. 這個(gè) iframe 的請(qǐng)求是可以把 sso cookie 送給 sso server 的。sso server 驗(yàn)證 cookie 后,返回一個(gè)重定向頁(yè)面到 app 的某個(gè) URL,由該 URL 設(shè)置 app cookie
D. 此時(shí)瀏覽器上可看見(jiàn)的頁(yè)面容器實(shí)際上也是可以和重定向回來(lái)的內(nèi)容交互的。比如可以用 js 控制發(fā)現(xiàn)重定向頁(yè)面成功返回后,就刷新整個(gè)頁(yè)面,讓它看起來(lái)和用戶(hù)登錄后訪(fǎng)問(wèn)沒(méi)有什么區(qū)別。
下面是真正的技巧:怎樣才能在 IE 里面跨域去設(shè)置 cookie
上述技術(shù)看起來(lái)是不是很好?但它的前提是所有的登錄都 post 到 sso server 上,認(rèn)證成功后再返回 app 頁(yè)面。可我接受到的需求之一就是要支持頁(yè)面無(wú)刷新登錄。
哈!就是說(shuō)本來(lái)在 chinaren.com 上提交登錄表單的 action 應(yīng)該是 passport.sohu.com 這個(gè) sso
server。可是在 AJAX 大潮下,chinaren 計(jì)劃采用 XMLHTTPRequest 提交,這個(gè)就麻煩了,因?yàn)槭遣荒芸缬騺?lái)提交的。
那么解決方法就是跨域產(chǎn)生 cookie,即 js 發(fā)現(xiàn)口令校驗(yàn)成功后,再在 passport.sohu.com 上種上合法的 cookie.
套用上面的跨域讀 cookie 的方案似乎很簡(jiǎn)單去推論:就是創(chuàng)建一個(gè)隱含的 iframe,讓那個(gè) iframe 去調(diào)用
passport.sohu.com 的 URL 來(lái)產(chǎn)生 cookie。很遺憾,此方法在 Fx 下工作的很好,但是不能在 IE 上應(yīng)用。(在
IE 狀態(tài)欄上顯示 cookie 隱私警告,紅色圓底白橫杠)
我試了很多很多方法,包括創(chuàng)建 、 node,包括用 js 設(shè)置,但都一次次被 IE 無(wú)情的擋在了瀏覽器外。google 之,也沒(méi)有任何真正可用的答案,中文網(wǎng)頁(yè)要么介紹的方法是錯(cuò)的,要么說(shuō)無(wú)解。
最后還是在 chinaren 一哥們的幫助下,翻出了他們所使用的,以和 alumni.sohu.com 交互的方法(不知道是哪位牛人發(fā)現(xiàn)的),只需要設(shè)置 P3P HTTP Header,在隱含 iframe 里面跨域設(shè)置 cookie 就可以成功。他們所用的內(nèi)容是:
P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"
最后是我做的一個(gè)小小的演示:cookie 怎么在 vmx.cn 和 dup2.net 之間交互
1. http://qiuyingbo.test.vmx.cn/cookie.php
2. 隨便輸入什么,點(diǎn) reset cookie,就可以看到 vmx.cn 的 cookie 已經(jīng)被設(shè)上了
3. 在該頁(yè)面點(diǎn)連接到 http://www.dup2.net/vmx/cookie.html
4. 點(diǎn)"get corss-domain cookie" .. (此時(shí) js 會(huì)去創(chuàng)建一個(gè)iframe,請(qǐng)求 qiuyingbo.test.vmx.cn ,返回頁(yè)面把 cookie 值作為 GET 參數(shù)重定向回 dup2.net 的另外一個(gè)URL。)
5. 點(diǎn) "display corss-domain cookie" .. 就可以看到 vmx.cn 的 cookie 了
6. 在該頁(yè)面的輸入框中輸入其它的值,然后點(diǎn) "set cross-domain cookie",該行為將主動(dòng)設(shè)置 vmx.cn 的 cookie
7. 點(diǎn)鏈接回到 http://qiuyingbo.test.vmx.cn/cookie.php ,就可以看到新的值了
網(wǎng)上看了別人介紹的一片文章,說(shuō)使用P3P可以完成跨域COOKIE操作,感覺(jué)很COOL,不過(guò)沒(méi)有提供源代碼,我胡亂寫(xiě)了一下,大家看看。
實(shí)際工作中,類(lèi)似這樣的要求很多,比如說(shuō),我們有兩個(gè)域名,我們想實(shí)現(xiàn)在一個(gè)域名登錄后,能自動(dòng)完成另一個(gè)域名的登錄,也就是PASSPORT的功能。
我只寫(xiě)一個(gè)大概,為了測(cè)試的方便,先編輯hosts文件,加入測(cè)試域名(C:\WINDOWS\system32\drivers\etc\hosts)
127.0.0.1 www.a.com
127.0.0.1 www.b.com
首先:創(chuàng)建 a_setcookie.php 文件,內(nèi)容如下:
<?php
//header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
setcookie("test", $_GET['id'], time()+3600, "/", ".a.com");
?>
然后:創(chuàng)建 a_getcookie.php 文件,內(nèi)容如下:
<?php
var_dump($_COOKIE);
?>
最后:創(chuàng)建 b_setcookie.php 文件,內(nèi)容如下:
<script src="http://www.a.com/a_setcookie.php?id=www.b.com"></script>
----------------------------
三個(gè)文件創(chuàng)建完畢后,我們通過(guò)瀏覽器依次訪(fǎng)問(wèn):
http://www.b.com/b_setcookie.php
http://www.a.com/a_getcookie.php
我們會(huì)發(fā)現(xiàn),在訪(fǎng)問(wèn)b.com域的時(shí)候,我們并沒(méi)有在a.com域設(shè)置上cookie值。
然后我們修改一下a_setcookie.php文件,去掉注釋符號(hào),a_setcookie.php即為:
<?php
header('P3P: CP="CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR"');
setcookie("test", $_GET['id'], time()+3600, "/", ".a.com");
?>
再次通過(guò)瀏覽器依次訪(fǎng)問(wèn):
http://www.b.com/b_setcookie.php
http://www.a.com/a_getcookie.php
這次,你會(huì)發(fā)現(xiàn)在訪(fǎng)問(wèn)b.com域的時(shí)候,我們?cè)O(shè)置了a.com域的cookie值。
末了補(bǔ)充一句,似乎只有IE對(duì)跨域訪(fǎng)問(wèn)COOKIE限制比較嚴(yán)格,上述代碼在FIREFOX下測(cè)試,即使不發(fā)送P3P頭信息,也能成功。不過(guò)IE是老大啊。
參考文檔:http://www.w3.org/P3P/