一:單點登入是什麼
單點登入(Single Sign On
),簡稱為 SSO
,是目前比較流行的企業業務整合的解決方案之一
SSO
的定義是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統SSO
一般都需要一個獨立的認證中心(passport
),子系統的登入均得透過 passport
,子系統本身將不參與登入操作
當一個系統成功登入以後,passport
將會頒發一個令牌給各個子系統,子系統可以拿著令牌會獲取各自的受保護資源,爲了減少頻繁認證,各個子系統在被 passport
授權以後,會建立一個區域性會話,在一定時間內可以無需再次向 passport
發起認證
上圖有四個系統,分別是Application1
、Application2
、Application3
、和SS0
,當Application1
、Application2
、Application3
需要登入時,將跳到 SS0
系統,SS0
系統完成登入,其他的應用系統也就隨之登入了
舉個例子
淘寶、天貓都屬於阿里旗下,當用戶登入淘寶後,再開啟天貓,系統便自動幫使用者登入了天貓,這種現象就屬於單點登入
二:如何實現
2.1.同域名下的單點登入
cookie
的 domain
屬性設定為當前域的父域,並且父域的 cookie
會被子域所共享。 path
屬性預設為 web
應用的上下文路徑
利用 Cookie
的這個特點,沒錯,我們只需要將Cookie
的 domain
屬性設定為父域的域名(主域名),同時將Cookie
的path
屬性設定為根路徑,將SessionID
(或Token
)儲存到父域中。這樣所有的子域應用就都可以訪問到這個Cookie
不過這要求應用系統的域名需建立在一個共同的主域名之下,如 tieba.baidu.com
和 map.baidu.com
,它們都建立在baidu.com
這個主域名之下,那麼它們就可以透過這種方式來實現單點登入
2.2.基於認證中心的SSO
如果是不同域的情況下,Cookie
是不共享的,這裏我們可以部署一個認證中心,用於專門處理登入請求的獨立的 Web
服務 使用者統一在認證中心進行登入,登入成功後,認證中心記錄使用者的登入狀態,並將token
寫入Cookie
(注意這個Cookie
是認證中心的,應用系統是訪問不到的)
應用系統檢查當前請求有沒有Token
,如果沒有,說明使用者在當前系統中尚未登入,那麼就將頁面跳轉至認證中心
由於這個操作會將認證中心的CookieCookie
知道使用者是自動帶過去,因此,認證中心能夠根據否已經登入過了
如果認證中心發現使用者尚未登入,則返回登入頁面,等待使用者登入
如果發現使用者已經登入過了,就不會讓使用者再次登入了,而是會跳轉回目標URL
,並在跳轉前生成一個 Token
,拼接在目標 URL
的後面,回傳給目標應用系統
應用系統拿到 Token
之後,還需要向認證中心確認下Token
的合法性,防止使用者偽造。確認無誤後,應用系統記錄使用者的登入狀態,並將Token
寫入Cookie
,然後給本次訪問放行。(注意這個Cookie
是當前應用系統的)當用戶再次訪問當前應用系統時,就會自動帶上這個 Token
,應用系統驗證 Token
發現使用者已登入,於是就不會有認證中心什麼事了
此種實現方式相對複雜,支援跨域,擴充套件性好,是單點登入的標準做法
2.3.基於LocalStorage的SSO
可以選擇將SessionID
(或Token )儲存到瀏覽器的 LocalStorage 中,讓前端在每次向後端傳送請求時,主動將LocalStorage
的資料傳遞給服務端
這些都是由前端來控制的,後端需要做的僅僅是在使用者登入成功後,將SessionID
(或 Token
)放在響應體中傳遞給前端
單點登入完全可以在前端實現。前端拿到SessionID
(或Token
)後,除了將它寫入自己的Localstorage
中之外,還可以透過特殊手段將它寫入多個其他域下的LocalStorag
e中
關鍵程式碼如下:
// 獲取 token var token =result.data.token; //動態建立一個不可見的iframe,在iframe中載入一個跨域HTMLvar iframe = document.createElement("iframe"); iframe.src ="http://app1.com/localstorage.html";document.body.append(iframe); //使用postMessage()方法將token傳遞給iframe setTimeout(function(){ iframe.contentWindow.postMessage(token, "http://app1.com"); },4000); setTimeout(function(){ iframe.remove(); },6000); //在這個iframe所載入的HTML中繫結一個事件監聽器,當事件被觸發時,把接收到的token資料寫入localstorage window.addEventListener('message',function(event){ localStorage.setItem(token',event.data) },false);
前端透過 iframe+postMessage()
方式,將同一份 Token
寫入到了多個域下的 LocalStorage
中,前端每次在向後端傳送請求之前,都會主動從LocalStorage
中讀取 Token
並在請求中攜帶,這樣就實現了同一份 Token
被多個域所共享
此種實現方式完全由前端控制,幾乎不需要後端參與,同樣支援跨域
三:流程
單點登入流程:
使用者訪問系統1的受保護資源,系統1發現使用者未登入,跳轉至sso認證中心,並將自己的地址作為引數
sso認證中心發現使用者未登入,將使用者引導至登入頁面
使用者輸入使用者名稱密碼提交登入申請
sso認證中心校驗使用者資訊,建立使用者與sso認證中心之間的會話,稱為全域性會話,同時建立授權令牌
sso認證中心帶著令牌跳轉會最初的請求地址(系統1)
系統1拿到令牌,去sso認證中心校驗令牌是否有效
sso認證中心校驗令牌,返回有效,註冊系統1
系統1使用該令牌建立與使用者的會話,稱為區域性會話,返回受保護資源
使用者訪問系統2的受保護資源
系統2發現使用者未登入,跳轉至sso認證中心,並將自己的地址作為引數
sso認證中心發現使用者已登入,跳轉回系統2的地址,並附上令牌
系統2拿到令牌,去sso認證中心校驗令牌是否有效
sso認證中心校驗令牌,返回有效,註冊系統2系統2
使用該令牌建立與使用者的區域性會話,返回受保護資源
使用者登入成功之後,會與sso認證中心及各個子系統建立會話,使用者與sso認證中心建立的會話稱為全域性會話
使用者與各個子系統建立的會話稱為區域性會話,區域性會話建立之後,使用者訪問子系統受保護資源將不再透過 sso 認證中心 全域性會話與區域性會話有如下約束關係:
區域性會話存在,全域性會話一定存在
全域性會話存在,區域性會話不一定存在
全域性會話銷燬,區域性會話必須銷燬