前端實現單點登錄(SSO)

普通登錄

提到單點登錄,首先可以想到傳統登錄,通過登錄頁面根據用戶名查詢用戶信息,判斷密碼是否正確,正確則將用戶信息寫到 session,訪問的時候通過從 session 中獲取用戶信息,判斷是否已登錄,登錄則允許訪問。

普通登錄的缺點

由於 session 不能共享,服務越來越多,並且還服務還搭建集羣,導致每訪問另外一個服務都需要重新登錄。

單點登錄

單點登錄有個簡稱是 sso,它是一個功能可以控制多個有聯繫的系統操作,簡單地理解爲通過單點登錄可以讓用戶只需要登錄一次軟件或者系統,那麼同系統下的平臺都可以免去再次註冊、驗證、訪問權限的麻煩程序,通俗易懂的理解爲一次性登錄也可以一次性下線。

前端需要知道的單點登錄概述:

1、一個系統登錄流程:用戶進入系統——未登錄——跳轉登錄界面——用戶名和密碼發送——服務器端驗證後,設置一個 cookie 發送到瀏覽器,設置一個 session 存放在服務器——用戶再次請求(帶上 cookie)——服務器驗證 cookie 和 session 匹配後,就可以進行業務了。

2、多個系統登錄:如果一個大公司有很多系統,a.seafile.com, b.seafile.com,c.seafile.com。這些系統都需要登錄,如果用戶在不同系統間登錄需要多次輸入密碼,用戶體驗很不好。所以使用 SSO (single sign on) 單點登錄實現。

3、相同域名,不同子域名下的單點登錄:在瀏覽器端,根據同源策略,不同子域名的 cookie 不能共享。所以設置 SSO 的域名爲根域名。SSO 登錄驗證後,子域名可以訪問根域名的 cookie,即可完成校驗。在服務器端,可以設置多個子域名 session 共享(Spring-session)

4、不同域名下的單點登錄:CAS 流程:用戶登錄子系統時未登錄,跳轉到 SSO 登錄界面,成功登錄後,SSO 生成一個 ST (service ticket )。用戶登錄不同的域名時,都會跳轉到 SSO,然後 SSO 帶着 ST 返回到不同的子域名,子域名中發出請求驗證 ST 的正確性(防止篡改請求)。驗證通過後即可完成不同的業務。

單點登錄需求

在項目初期,公司中使用的系統很少,通常一個或者兩個,每個系統都有自己的登錄系統,用戶用自己的賬號登錄,很方便。

但隨着公司的不斷髮展,用到的系統隨之增多,用戶在操作不同的系統時,需要多次登錄,而且每個系統的賬號都不一樣,這對於用戶來說,是很不好的體驗。於是,就想到是不是可以在一個系統登錄,其他系統就不用登錄了呢?這就是單點登錄要解決的問題。

單點登錄英文全稱 Single Sign On,簡稱就是 SSO。它的解釋是:在多個應用系統中,只需要登錄一次,就可以訪問其他相互信任的應用系統。

單點登錄(Single Sign On),簡稱爲 SSO,是比較流行的企業業務整合的解決方案之一。SSO 的定義是在多個應用系統中,用戶只需要登錄一次就可以訪問所有相互信任的應用系統。

如圖所示,圖中有 4 個系統,分別是 Application1、Application2、Application3、和 SSO。Application1、Application2、Application3 沒有登錄模塊,而 SSO 只有登錄模塊,沒有其他的業務模塊,當 Application1、Application2、Application3 需要登錄時,將跳到 SSO 系統,SSO 系統完成登錄,其他的應用系統也就隨之登錄了。這完全符合我們對單點登錄(SSO)的定義。

SSO 機制實現流程

用戶首次訪問時,需要在認證中心登錄:

  1. 用戶訪問網站 a.com 下的 pageA 頁面。

  2. 由於沒有登錄,則會重定向到認證中心,並帶上回調地址 (http://www.sso.com?return_uri=a.com/pageA%EF%BC%8C%E4%BB%A5%E4%BE%BF%E7%99%BB%E5%BD%95%E5%90%8E%E7%9B%B4%E6%8E%A5%E8%BF%9B%E5%85%A5%E5%AF%B9%E5%BA%94%E9%A1%B5%E9%9D%A2%E3%80%82 )

  3. 用戶在認證中心輸入賬號密碼,提交登錄。

  4. 認證中心驗證賬號密碼有效,然後重定向 a.com?ticket=123 帶上授權碼 ticket,並將認證中心 sso.com 的登錄態寫入 Cookie。

  5. 在 a.com 服務器中,拿着 ticket 向認證中心確認,授權碼 ticket 真實有效。

  6. 驗證成功後,服務器將登錄信息寫入 Cookie(此時客戶端有 2 個 Cookie 分別存有 a.com 和 sso.com 的登錄臺)。

認證中心登錄完成之後,繼續訪問 a.com 下的其他頁面:

這個時候,由於 a.com 存在已登錄的 Cookie 信息,所以服務器端直接認證成功。

如果認證中心登錄完成之後,訪問 b.com 下的頁面:

這個時候,由於認證中心存在之前登錄過的 Cookie,所以也不用再次輸入賬號密碼,直接返回第 4 步,下發 ticketb.com 即可。

SSO 機制實現方式

單點登錄主要有三種實現方式:

  1. 父域 Cookie

  2. 認證中心

  3. LocalStorage 跨域

一般情況下,用戶的登錄狀態是記錄在 Session 中的,要實現共享登錄狀態,就要先共享 Session,但是由於不同的應用系統有着不同的域名,儘管 Session 共享了,但是由於 SessionId 是往往保存在瀏覽器 Cookie 中的,因此存在作用域的限制,無法跨域名傳遞,也就是說當用戶在 a.com 中登錄後,Session Id 僅在瀏覽器訪問 a.com 時纔會自動在請求頭中攜帶,而當瀏覽器訪問 b.com 時,Session Id 是不會被帶過去的。實現單點登錄的關鍵在於,如何讓 Session Id(或 Token)在多個域中共享。

1. 父域 Cookie

Cookie 的作用域由 domain 屬性和 path 屬性共同決定。domain 屬性的有效值爲當前域或其父域的域名 / IP 地址,在 Tomcat 中,domain 屬性默認爲當前域的域名 / IP 地址。path 屬性的有效值是以 “/” 開頭的路徑,在 Tomcat 中,path 屬性默認爲當前 Web 應用的上下文路徑。

如果將 Cookie 的 domain 屬性設置爲當前域的父域,那麼就認爲它是父域 Cookie。Cookie 有一個特點,即父域中的 Cookie 被子域所共享,也就是說,子域會自動繼承父域中的 Cookie。

利用 Cookie 的這個特點,可以將 Session Id(或 Token)保存到父域中就可以了。我們只需要將 Cookie 的 domain 屬性設置爲父域的域名(主域名),同時將 Cookie 的 path 屬性設置爲根路徑,這樣所有的子域應用就都可以訪問到這個 Cookie 了。不過這要求應用系統的域名需建立在一個共同的主域名之下,如 tieba.baidu.com 和 map.baidu.com,它們都建立在 baidu.com 這個主域名之下,那麼它們就可以通過這種方式來實現單點登錄。

總結:此種實現方式比較簡單,但不支持跨主域名。

2. 認證中心

我們可以部署一個認證中心,認證中心就是一個專門負責處理登錄請求的獨立的 Web 服務。

用戶統一在認證中心進行登錄,登錄成功後,認證中心記錄用戶的登錄狀態,並將 Token 寫入 Cookie。(注意這個 Cookie 是認證中心的,應用系統是訪問不到的)

應用系統檢查當前請求有沒有 Token,如果沒有,說明用戶在當前系統中尚未登錄,那麼就將頁面跳轉至認證中心進行登錄。由於這個操作會將認證中心的 Cookie 自動帶過去,因此,認證中心能夠根據 Cookie 知道用戶是否已經登錄過了。如果認證中心發現用戶尚未登錄,則返回登錄頁面,等待用戶登錄,如果發現用戶已經登錄過了,就不會讓用戶再次登錄了,而是會跳轉回目標 URL ,並在跳轉前生成一個 Token,拼接在目標 URL 的後面,回傳給目標應用系統。

應用系統拿到 Token 之後,還需要向認證中心確認下 Token 的合法性,防止用戶僞造。確認無誤後,應用系統記錄用戶的登錄狀態,並將 Token 寫入 Cookie,然後給本次訪問放行。(這個 Cookie 是當前應用系統的,其他應用系統是訪問不到的)當用戶再次訪問當前應用系統時,就會自動帶上這個 Token,應用系統驗證 Token 發現用戶已登錄,於是就不會有認證中心什麼事了。

總結:此種實現方式相對複雜,支持跨域,擴展性好,是單點登錄的標準做法。

3. LocalStorage 跨域

單點登錄的關鍵在於,如何讓 Session Id(或 Token)在多個域中共享。但是 Cookie 是不支持跨主域名的,而且瀏覽器對 Cookie 的跨域限制越來越嚴格。

在前後端分離的情況下,完全可以不使用 Cookie,我們可以選擇將 Session Id (或 Token )保存到瀏覽器的 LocalStorage 中,讓前端在每次向後端發送請求時,主動將 LocalStorage 的數據傳遞給服務端。這些都是由前端來控制的,後端需要做的僅僅是在用戶登錄成功後,將 Session Id (或 Token )放在響應體中傳遞給前端。

在這樣的場景下,單點登錄完全可以在前端實現。前端拿到 Session Id (或 Token )後,除了將它寫入自己的 LocalStorage 中之外,還可以通過特殊手段將它寫入多個其他域下的 LocalStorage 中。

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

SSO 單點登錄退出

目前我們已經完成了單點登錄,在同一套認證中心的管理下,多個產品可以共享登錄態。現在我們需要考慮退出了,即:在一個產品中退出了登錄,怎麼讓其他的產品也都退出登錄?

原理其實不難,可以在每一個產品在向認證中心驗證 ticket(token) 時,其實可以順帶將自己的退出登錄 api 發送到認證中心。

當某個產品 c.com 退出登錄時:

清空 c.com 中的登錄態 Cookie。請求認證中心 sso.com 中的退出 api。認證中心遍歷下發過 ticket(token) 的所有產品,並調用對應的退出 api,完成退出。

作者:愛喫橘子的程序猿 

https://juejin.cn/post/7282692430117748755

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