單點登陸在如今的系統架構中普遍存在,他將多個子系統的認證體系打通,實現了一個入口多處使用,而在架構單點登陸時,也會遇到一些小問題,在不一樣的應用環境中能夠採用不一樣的單點登陸實現方案來知足需求。跨域
我將以我所遇到的應用環境以及在其中所經歷的各個階段與你們分享,如有不足,但願各位不吝賜教。安全
共享Session可謂是實現單點登陸最直接、最簡單的方式。將用戶認證信息保存於Session中,即以Session內存儲的值爲用戶憑證,這在單個站點內使用是很正常也很容易實現的,而在用戶驗證、用戶信息管理與業務應用分離的場景下即會遇到單點登陸的問題,在應用體系簡單,子系統不多的狀況下,能夠考慮採用Session共享的方法來處理這個問題。微信
這個架構我使用了基於Redis的Session共享方案。將Session存儲於Redis上,而後將整個系統的全局Cookie Domain設置於頂級域名上,這樣SessionID就能在各個子系統間共享。架構
這個方案存在着嚴重的擴展性問題,首先,ASP.NET的Session存儲必須爲SessionStateItemCollection對象,而存儲的結構是通過序列化後通過加密存儲的。加密
而且當用戶訪問應用時,他首先作的就是將存儲容器裏的全部內容所有取出,而且反序列化爲SessionStateItemCollection對象。這就決定了他具備如下約束:spa
這種單點登陸將用戶的身份標識信息簡化爲OpenId存放於客戶端,當用戶登陸某個子系統時,將OpenId傳送到服務端,服務端根據OpenId構造用戶驗證信息,多用於C/S與B/S相結合的系統,流程以下:對象
由上圖能夠看到,這套單點登陸依賴於OpenId的傳遞,其驗證的基礎在於OpenId的存儲以及發送。blog
這套單點登陸驗證機制的主要問題在於他基於C/S架構下將用戶的OpenId存儲於客戶端,在子系統之間發送OpenId,而B/S模式下要作到這一點就顯得較爲困難。爲了處理這個問題咱們將引出下一種方式,這種方式將解決B/S模式下的OpenId的存儲、傳遞問題。接口
咱們知道,Cookie的做用在於充當一個信息載體在Server端和Browser端進行信息傳遞,而Cookie通常是以域名爲分割的,例如a.xxx.com與b.xxx.com的Cookie是不能互相訪問的,可是子域名是能夠訪問上級域名的Cookie的。即a.xxx.com和b.xxx.com是能夠訪問xxx.com下的Cookie的,因而就能將頂級域名的Cookie做爲OpenId的載體。圖片
驗證步驟和上第二個方法很是類似:
在以上兩種方法中咱們均可以看到經過OpenId解耦了Session共享方案中的類型等問題,而且構造用戶驗證信息將更靈活,子系統間的驗證是相互獨立的,可是在第三種方案裏,咱們基於全部子系統都是同一個頂級域名的假設,而在實際生產環境裏有多個域名是很正常的事情,那麼就不得不考慮跨域問題究竟如何解決。
在多個頂級域名的狀況下,咱們將沒法讓各個子系統的OpenId共享。處理B/S環境下的跨域問題,咱們首先就應該想到JSONP的方案。
驗證步驟以下:
通過以上步驟,跨域狀況下的單點登陸問題已經能夠獲得解決。而在整個開發過程初期,咱們採用用戶表中記錄一個OpenId字段來保存用戶OpenId,而這個機制下很明顯存在一些安全性、擴展性問題。這個擴展性問題主要體如今一個方面:OpenId的安全性和用戶體驗的矛盾。
整個單點登陸的機制決定了OpenId是會出如今客戶端的,因此OpenId須要有過時機制,假如用戶在一個終端登陸的話能夠選擇在用戶每次登陸或者每次退出時刷新OpenId,而在多終端登陸的狀況下就會出現矛盾:當一個終端刷新了OpenId以後其餘終端將沒法正常受權。
而最終,我採用了單用戶多OpenId的解決方案。每次用戶經過用戶名/密碼登陸時,產生一個OpenId保存在Redis裏,而且設定過時時間,這樣多個終端登陸就會有多個OpenId與之對應,再也不會存在一個OpenId失效全部終端驗證都失效的狀況。