什麼是單點登錄?如何實現?

一、是什麼

單點登錄(Single Sign On),簡稱爲 SSO,是目前比較流行的企業業務整合的解決方案之一

SSO 的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統

SSO 一般都需要一個獨立的認證中心(passport),子系統的登錄均得通過passport,子系統本身將不參與登錄操作

當一個系統成功登錄以後,passport將會頒發一個令牌給各個子系統,子系統可以拿着令牌會獲取各自的受保護資源,爲了減少頻繁認證,各個子系統在被passport授權以後,會建立一個局部會話,在一定時間內可以無需再次向passport發起認證

上圖有四個系統,分別是Application1Application2Application3、和SSO,當Application1Application2Application3需要登錄時,將跳到SSO系統,SSO系統完成登錄,其他的應用系統也就隨之登錄了

舉個例子

淘寶、天貓都屬於阿里旗下,當用戶登錄淘寶後,再打開天貓,系統便自動幫用戶登錄了天貓,這種現象就屬於單點登錄

二、如何實現

同域名下的單點登錄

cookiedomin屬性設置爲當前域的父域,並且父域的cookie會被子域所共享。path屬性默認爲web應用的上下文路徑

利用 Cookie 的這個特點,沒錯,我們只需要將Cookiedomain屬性設置爲父域的域名(主域名),同時將 Cookiepath屬性設置爲根路徑,將 Session ID(或 Token)保存到父域中。這樣所有的子域應用就都可以訪問到這個Cookie

不過這要求應用系統的域名需建立在一個共同的主域名之下,如 tieba.baidu.commap.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 被多個域所共享

此種實現方式完全由前端控制,幾乎不需要後端參與,同樣支持跨域

三、流程

單點登錄的流程圖如下所示:

用戶登錄成功之後,會與sso認證中心及各個子系統建立會話,用戶與sso認證中心建立的會話稱爲全局會話

用戶與各個子系統建立的會話稱爲局部會話,局部會話建立之後,用戶訪問子系統受保護資源將不再通過sso認證中心

全局會話與局部會話有如下約束關係:

參考文獻

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/a9VLbs555jCtNr0gJPOMpg