什麼是單點登錄?如何實現?
一、是什麼
單點登錄(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一
SSO 的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統
SSO 一般都需要一個獨立的認證中心(passport),子系統的登錄均得通過passport
,子系統本身將不參與登錄操作
當一個系統成功登錄以後,passport
將會頒發一個令牌給各個子系統,子系統可以拿着令牌會獲取各自的受保護資源,爲了減少頻繁認證,各個子系統在被passport
授權以後,會建立一個局部會話,在一定時間內可以無需再次向passport
發起認證
上圖有四個系統,分別是Application1
、Application2
、Application3
、和SSO
,當Application1
、Application2
、Application3
需要登錄時,將跳到SSO
系統,SSO
系統完成登錄,其他的應用系統也就隨之登錄了
舉個例子
淘寶、天貓都屬於阿里旗下,當用戶登錄淘寶後,再打開天貓,系統便自動幫用戶登錄了天貓,這種現象就屬於單點登錄
二、如何實現
同域名下的單點登錄
cookie
的domin
屬性設置爲當前域的父域,並且父域的cookie
會被子域所共享。path
屬性默認爲web
應用的上下文路徑
利用 Cookie
的這個特點,沒錯,我們只需要將Cookie
的domain
屬性設置爲父域的域名(主域名),同時將 Cookie
的path
屬性設置爲根路徑,將 Session ID
(或 Token
)保存到父域中。這樣所有的子域應用就都可以訪問到這個Cookie
不過這要求應用系統的域名需建立在一個共同的主域名之下,如 tieba.baidu.com
和 map.baidu.com
,它們都建立在 baidu.com
這個主域名之下,那麼它們就可以通過這種方式來實現單點登錄
不同域名下的單點登錄 (一)
如果是不同域的情況下,Cookie
是不共享的,這裏我們可以部署一個認證中心,用於專門處理登錄請求的獨立的 Web
服務
用戶統一在認證中心進行登錄,登錄成功後,認證中心記錄用戶的登錄狀態,並將 token
寫入 Cookie
(注意這個 Cookie
是認證中心的,應用系統是訪問不到的)
應用系統檢查當前請求有沒有 Token
,如果沒有,說明用戶在當前系統中尚未登錄,那麼就將頁面跳轉至認證中心
由於這個操作會將認證中心的 Cookie
自動帶過去,因此,認證中心能夠根據 Cookie
知道用戶是否已經登錄過了
如果認證中心發現用戶尚未登錄,則返回登錄頁面,等待用戶登錄
如果發現用戶已經登錄過了,就不會讓用戶再次登錄了,而是會跳轉回目標 URL
,並在跳轉前生成一個 Token
,拼接在目標URL
的後面,回傳給目標應用系統
應用系統拿到 Token
之後,還需要向認證中心確認下 Token
的合法性,防止用戶僞造。確認無誤後,應用系統記錄用戶的登錄狀態,並將 Token
寫入Cookie
,然後給本次訪問放行。(注意這個 Cookie
是當前應用系統的)當用戶再次訪問當前應用系統時,就會自動帶上這個 Token
,應用系統驗證 Token 發現用戶已登錄,於是就不會有認證中心什麼事了
此種實現方式相對複雜,支持跨域,擴展性好,是單點登錄的標準做法
不同域名下的單點登錄 (二)
可以選擇將 Session ID
(或 Token
)保存到瀏覽器的 LocalStorage
中,讓前端在每次向後端發送請求時,主動將LocalStorage
的數據傳遞給服務端
這些都是由前端來控制的,後端需要做的僅僅是在用戶登錄成功後,將 Session ID
(或 Token
)放在響應體中傳遞給前端
單點登錄完全可以在前端實現。前端拿到 Session ID
(或 Token
)後,除了將它寫入自己的 LocalStorage
中之外,還可以通過特殊手段將它寫入多個其他域下的 LocalStorage
中
關鍵代碼如下:
// 獲取 token
var token = result.data.token;
// 動態創建一個不可見的iframe,在iframe中加載一個跨域HTML
var 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
認證中心
全局會話與局部會話有如下約束關係:
-
局部會話存在,全局會話一定存在
-
全局會話存在,局部會話不一定存在
-
全局會話銷燬,局部會話必須銷燬
參考文獻
-
https://blog.csdn.net/weixin_36380516/article/details/109006828
-
https://baike.baidu.com/item/%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95
-
https://juejin.cn/post/6844903664985866253
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/a9VLbs555jCtNr0gJPOMpg