單點登入( Single Sign On ,簡稱 SSO),是目前比較流行的企業業務整合的解決方案之一,用於多個應用系統間,使用者只需要登入一次就可以訪問所有相互信任的應用系統。
圖片來自 Pexels
前置介紹:
同源策略,限制了從同一個源載入的文件或指令碼如何與來自另一個源的資源進行互動,要求協議,埠和主機都相同。HTTP 用於分散式、協作式和超媒體資訊系統的應用層協議。HTTP 是無狀態協議,所以伺服器單從網路連線上無從知道客戶身份。那要如何才能識別客戶端呢?給每個客戶端頒發一個通行證,每次訪問時都要求帶上通行證,這樣伺服器就可以根據通行證識別客戶了。最常見的方案就是 Cookie。Cookie 是客戶端儲存使用者資訊的一種機制,儲存在客戶機硬碟上。可以由伺服器響應報文 Set-Cookie 的首部欄位資訊或者客戶端 document.cookie來設定,並隨著每次請求傳送到伺服器。子域名可以獲取父級域名 Cookie。Session 其實是一個抽象概念,用於跟蹤會話,識別多次 HTTP 請求來自同一個客戶端。Cookie 只是通用性較好的一種實現方案,通常是設定一個名為 SessionID(名稱可自定義,便於描述,本文均使用此名稱)的 Cookie,每次請求時攜帶該 Cookie,後臺服務即可依賴此 SessionID 值識別客戶端。單系統登入
在介紹單點登入之前,我們先來了解一下在瀏覽器中,訪問一個需要登入的應用時主要發生的一系列流程,如下圖所示:
以下為連環畫形式,期望能讓讀者更好的理解:
依賴於登入後設置的 Cookie,之後每次訪問時都會攜帶該 Cookie,從而讓後臺服務能識別當前登入使用者。
題外話:後臺是如何通過 SessionID 知道是哪個使用者呢?
資料庫儲存關聯:將 SessionID 與資料資訊關聯,儲存在 Redis、MySQL 等資料庫中。資料加密直接儲存:比如 JWT 方式,使用者資料直接從 SessionID 值解密出來(此方式時 Cookie 名稱以 Token 居多)。多系統登入問題
同域名
當訪問同域名下的頁面時,Cookie 和單系統登入時一樣,會正常攜帶,後臺服務即可直接獲取到對應的 SessionID 值,後臺為單服務還是多服務無差別。
不同子域名
子域名間 Cookie 是不共享的,但各子域名均可獲取到父級域名的 Cookie,即 app.demo.com與 news.demo.com均可以獲取 demo.com域名下的 Cookie。
所以可以通過將 Cookie 設定在父級域名上,可以達到子域名共享的效果,即當用戶在 app.demo.com 域名下登入時,在demo.com域名下設定名為 SessionID 的 Cookie。
當用戶之後訪問news.demo.com時,後臺服務也可以獲取到該 SessionID,從而識別使用者。
完全不同域名
預設情況下,不同域名是無法直接共享 Cookie 的。
前端跨域帶 Cookie
如果只是期望非同步請求時獲取當前使用者的登入態,可以通過傳送跨域請求到已經登入過的域名,並配置屬性:
xhrFields: { withCredentials: true }
這樣可在請求時攜帶目標域名的 Cookie,目標域名的服務即可識別當前使用者。
但是,這要求目標域名的介面支援 CORS 訪問(出於安全考慮,CORS 開啟 withCredentials 時,瀏覽器不支援使用萬用字元*,需明確設定可跨域訪問的域名名單)。
題外話:如果只是為了規避瀏覽器的限制,實現與通配*同樣的效果,到達所有域名都可以訪問的目的,可根據訪問的 Referrer 解析請求來源域名,作為可訪問名單。但是出於安全考慮,不推薦使用,請設定明確的可訪問域名。
CAS
CAS(Central Authentication Service),即中央認證服務,是 Yale 大學發起的一個開源專案,旨在為 Web 應用系統提供一種可靠的單點登入方法。
既然不能跨域獲取,那 CAS 如何做到共享呢?它通過跳轉中間域名的方式來實現登入。
頁面訪問流程如下圖:
以下為連環畫形式,期望能讓讀者更好的理解:
其中需要關注以下 2 點:
所有的登入過程都依賴於 CAS 服務,包含使用者登入頁面、ST 生成、驗證。為了保證 ST 的安全性,一般 ST 都是隨機生成的,沒有規律性。CAS 規定 ST 只能保留一定的時間,之後 CAS 服務會讓它失效,而且,CAS 協議規定 ST 只能使用一次,無論 ST 驗證是否成功,CAS 服務都會清除服務端快取中的該 ST,從而規避同一個 ST 被使用兩次或被竊取的風險。