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

林恒發表於2024-05-20

一、是什麼

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

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

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

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

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

舉個例子

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

二、如何實現

同域名下的單點登入

cookiedomain屬性設定為當前域的父域,並且父域的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 被多個域所共享

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

三、流程

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

  • 使用者訪問系統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

如果對您有所幫助,歡迎您點個關注,我會定時更新技術文件,大家一起討論學習,一起進步。

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

相關文章