SSO (單點登錄)實現方式

SSO (Single-Sign-On) 即單點登錄,在互聯網應用中是多個站點通過一次登錄即可訪問所有產品,如Google所有產品通過 http://accounts.google.com/,百度所有產品統一登錄地點是 http://passport.baidu.com/ 等,也有些產品是提供自己的登錄界面,然後到統一入口驗證。總之,就是要實現一次登錄,處處登錄。

如果所有產品都是在同一主域下,那麼只要把登錄的標識信息存放到主域的 cookie 中,即可實現訪問任一產品的頁面時把登錄信息傳遞到服務器,然後服務器根據該信息判斷是否需要用戶再次登錄。雖然大多數公司的產品都是在同一個域名下,但有些還是獨立域名的,這時就涉及到跨域問題,也是 SSO 的難點所在。

基本思路是在一個固定的入口登錄,成功後返回一個 token,將這個 token 附帶在跨域訪問的某個文件上,該文件拿到這個 token 和服務器上存放的值比較,並獲取對應的登錄用戶信息,然後設置登錄標識 cookie,以此完成 SSO 登錄。服務器上的 token 可以存在 memcache 等緩存中,或者通過 RPC 訪問。

通過查看現有網站,主要是百度和新浪的 SSO 實現方案(截至2013-7-31),來對 SSO 實現方式有一個詳細的瞭解。

百度的 SSO

百度的很多產品都是在 baidu.com 這個主域名下,納入到 SSO 的其他獨立域名目前只有 hao123.com(很討厭這個網站,一不小心就會在裝軟件時將它設爲默認首頁,還好現在不怎麼用 IE)。

點擊百度產品的登錄,一般會跳轉到 passport.baidu.com,在這個頁面完成登錄,也有些是浮層,但登錄實現方式一樣:

  1. 填寫完用戶名密碼後,點擊“登錄”按鈕。

  2. js 創建一個 div 容器,在這個容器中主要創建兩個元素:form 表單和 iframe。

    • from 中包含很多 type="hidden" 的 <input/> 標記,主要包含填寫內容和其他相關內容,如要登錄的產品標識和成功後的跳轉地址等。form 的提交目標窗口指向之後的 iframe。

    • iframe 初始地址所一個 _blank.html 文件,會造成一次 http 請求。

    • 這些內容都是拼成一個字符串,一次性寫入的。

  3. 動態創建節點完成後,馬上提交數據,指向 iframe 中 passport.baidu.com/v2/api/?login。

  4. 提交返回的結果內容在 iframe 中,通過 js 中的 'location.replace()' 跳轉 iframe 到一個新的頁面,這個頁面是第2步中的表單項 'staticpage' 指定的,一般都叫 v3Jump.hmtl,不同產品只是路徑不同。

  5. v3Jump.html 中的 js 的作用就是以形如 parent.someMethod({}) 的方式調用 iframe 父級窗口的 js 方法。涉及到不同域名調用主窗口 js 方法,因此一般 v3Jump.html 都是放在和主窗口同一域名下。

  6. 主窗口的 js 處理頁面表現,同時爲了實現跨域,會調用 https://user.hao123.com/static/crossdomain.php?bdu=...&t=3434312,這個 php 會根據 bdu 值驗證登錄狀態,並設置 hao123.com 這個域名的 cookie 登錄標識。比較巧妙的是,百度把這個地址作爲一個 Image 對象的 src 屬性,這樣能完成請求,還不會渲染到頁面上。

  7. 最後,主窗口的 js 跳轉頁面到指定的地址。

新浪的 SSO

新浪的獨立域名好像只有一個 weibo.com,其他產品都是在 sina.com.cn 下。它的登錄方式與百度基本相同,這裏挑選不同的幾點說明。

在 weibo.com 登錄

  1. 同百度

  2. 同百度

  3. 提交數據到 iframe, 指向 login.sina.com.cn/sso/login.php,提交表單後,會立馬刪除 from 節點(百度只在登錄失敗再次提交時才替換這個 form 節點)

  4. 如果登錄失敗,iframe 中的內容爲 location.replace() 跳轉到 weibo.com/ajaxlogin.php;如果登錄成功,則跳轉地址爲 weibo.com/sso/login.php,該文件返回 302,跳轉到 ajaxlogin.php。

  5. ajaxlogin.php 負責用形如 parent.someMethod({}) 方式調用父窗口 js,處理頁面。

  6. 父窗口 js 刪除 iframe 節點

  7. 同百度

新浪其他 sina.com.cn 子域登錄

  1. 同 weibo

  2. 同 weibo

  3. 同 weibo,但不會刪除 form 節點

  4. 這裏有些不同,login.sina.com.cn 下,login.php 的內容用 location.replace() 指向 login.sina.com.cn/crossdomain2.php,由這個頁面調用 parent.someMethod();而在新浪首頁,login.php 中先設置 document.domain='sina.com.cn'(login.php 與頁面域名不完全相同),然後再調用 parent.someMethod()

  5. 父窗口的 js 處理頁面表現,並用jsonp方式(放到script標記的src中)調用 weibo.com/sso/login.php 來完成跨域登錄。

  6. 結束

新浪的登錄 js 並不統一,明顯 weibo 的 js 處理的更精細。

weibo 登錄中的第 4 步,iframe 中跳轉方式實現對跨域的訪問,顯然有些侷限。如果還有其他獨立域名需要訪問,這種跳轉將無法兼顧。

總結

上面兩個站點的實現方式基本相同,都是用 iframe 登錄,然後在 iframe 中跳轉來達到通過同一入口進行登錄。難點在於跨域,特別需要注意的是:

  • iframe 中的 js 如果要訪問父窗口的方法,需要保證域名相同,或者主域相同並都設置domain屬性爲主域地址,否則瀏覽器會報安全警告。

  • 跨域只要發起跨域的http請求即可,可以放到 script 標記的src中,也可已放到圖片對象的 src 中,推薦後者,因爲後者不用放到dom節點中。需要注意的是,這兩種方式都要帶一個每次都不同的參數,可以是時間戳,以防止瀏覽器的自動緩存。

上面着重關注了訪問的跳轉順序,還有一個關鍵點是如何標識登錄狀態。上面的方案中,都會在請求跨域文件時,帶上登錄返回的一個唯一字符串,將它作爲一個 token,在後端驗證登錄,識別登錄的用戶。可以把這個 token 設置爲很短的時間有效,如1分鐘,並在訪問一次後刪除,可以保證一定的安全性,但是如果在使用前截取,那麼可以拿到任何一個地方登錄。因爲上面兩個網站對跨域文件的訪問都是寫在主窗口的js裏,因此可以很容易設置斷點獲取之。

據說 Google 在這點上是安全的,可惜身邊沒法翻牆,留待以後吧。

補記

*2013-8-4

週末有空折騰了一下翻牆,找到個超級好東西:SmartHosts,Youtube/Facebook/twitter... 都可以上了。

Google SSO 實現方式

Google 所有產品的登錄都是通過 https://accounts.google.com/ 進行處理,基本流程如下:

  1. 在 Google 產品站點擊“登錄”,會跳轉到 https://accounts.google.com/ServiceLogin 登錄;

  2. 表單 post 提交到 https://accounts.google.com/ServiceLoginAuth 驗證;

  3. 登錄成功則直接通過 302 轉到 https://accounts.google.com/CheckCookie,訪問跨域產品的頁面,一般是 accounts.hostname/accounts/SetSID,設置登錄cookie;

  4. 頁面跳轉回登錄來源頁。

針對不同的來源,具體處理上在第3步有些差別:

  • 如果登錄來源是 *.google.com 或 youtube.com,則直接通過302指向 accounts.youtube.com/accounts/SetSID,之後該地址再轉向到來源地址。

  • 如果登錄來源是 *.google.com.hk,則返回200,通過 jsonp 請求 accounts.google.com.hk/accounts/SetSID 和 accounts.youtube.com/accounts/SetSID

可以看到,基本原理是一樣的,都是登錄後,訪問一下跨域的頁面,完成登錄信息的cookie設置。

過程中通過修改 accounts.youtube.com 的 ip 指向,截取到了登錄後的 /accounts/SetSID 鏈接,直接在一個新的瀏覽器中訪問,是可以完成登錄的。因此,Google 的 token 也不能保證這種截取的安全性。

-EOF-

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章