一、前言
1、SSO說明
SSO英文全稱Single Sign On,單點登錄,SSO是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統,https://baike.baidu.com/item/SSO/3451380
例如訪問在網易賬號中心(http://reg.163.com/ )登錄之后
訪問以下站點都是登錄狀態
- 網易直播 http://v.163.com
- 網易博客 http://blog.163.com
- 網易花田 http://love.163.com
- 網易考拉 https://www.kaola.com
- 網易Lofter http://www.lofter.com
2、設計目標
本篇文章也主要是為了探討如何設計&實作一個SSO系統
以下為需要實作的核心功能:
- 單點登錄
- 單點登出
- 支持跨域單點登錄
- 支持跨域單點登出
二、SSO設計與實作
1、核心應用與依賴

| 應用/模塊/物件 | 說明 |
|---|---|
| 前臺站點 | 需要登錄的站點 |
| SSO站點-登錄 | 提供登錄的頁面 |
| SSO站點-登出 | 提供注銷登錄的入口 |
| SSO服務-登錄 | 提供登錄服務 |
| SSO服務-登錄狀態 | 提供登錄狀態校驗/登錄資訊查詢的服務 |
| SSO服務-登出 | 提供用戶注銷登錄的服務 |
| 資料庫 | 存盤用戶賬戶資訊 |
| 快取 | 存盤用戶的登錄資訊,通常使用Redis |
2、用戶登錄狀態的存盤與校驗
常見的Web框架對于Session的實作都是生成一個SessionId存盤在瀏覽器Cookie中,然后將Session內容存盤在服務器端記憶體中,這個 ken.io 在之前Session作業原理中也提到過,整體也是借鑒這個思路,
用戶登錄成功之后,生成AuthToken交給客戶端保存,如果是瀏覽器,就保存在Cookie中,如果是手機App就保存在App本地快取中,本篇主要探討基于Web站點的SSO,
用戶在瀏覽需要登錄的頁面時,客戶端將AuthToken提交給SSO服務校驗登錄狀態/獲取用戶登錄資訊
對于登錄資訊的存盤,建議采用Redis,使用Redis集群來存盤登錄資訊,既可以保證高可用,又可以線性擴充,同時也可以讓SSO服務滿足負載均衡/可伸縮的需求,
| 物件 | 說明 |
|---|---|
| AuthToken | 直接使用UUID/GUID即可,如果有驗證AuthToken合法性需求,可以將UserName+時間戳加密生成,服務端解密之后驗證合法性 |
| 登錄資訊 | 通常是將UserId,UserName快取起來 |
3、用戶登錄/登錄校驗
- 登錄時序圖

按照上圖,用戶登錄后Authtoken保存在Cookie中, domian= test. com
瀏覽器會將domain設定成 .test.com,
這樣訪問所有*.test.com的web站點,都會將Authtoken攜帶到服務器端,
然后通過SSO服務,完成對用戶狀態的校驗/用戶登錄資訊的獲取
- 登錄資訊獲取/登錄狀態校驗

4、用戶登出
用戶登出時要做的事情很簡單:
- 服務端清除快取(Redis)中的登錄狀態
- 客戶端清除存盤的AuthToken
- 登出時序圖

5、跨域登錄、登出
前面提到過,核心思路是客戶端存盤AuthToken,服務器端通過Redis存盤登錄資訊,由于客戶端是將AuthToken存盤在Cookie中的,所以跨域要解決的問題,就是如何解決Cookie的跨域讀寫問題,
解決跨域的核心思路就是:
-
登錄完成之后通過回呼的方式,將AuthToken傳遞給主域名之外的站點,該站點自行將AuthToken保存在當前域下的Cookie中,
-
登出完成之后通過回呼的方式,呼叫非主域名站點的登出頁面,完成設定Cookie中的AuthToken過期的操作,
-
跨域登錄(主域名已登錄)

-
跨域登錄(主域名未登錄)

-
跨域登出

三、備注
- 關于方案
這次設計方案更多是提供實作思路,如果涉及到APP用戶登錄等情況,在訪問SSO服務時,增加對APP的簽名驗證就好了,當然,如果有無線網關,驗證簽名不是問題,
- 關于時序圖
時序圖中并沒有包含所有場景,ken.io只列舉了核心/主要場景,另外對于一些不影響理解思路的訊息能省就省了,
作者:ken.io
鏈接:SSO 單點登錄看這篇就夠了!
來源:github
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/230156.html
標籤:Java
