不管是什麼公司,隻要産品數量大于一個,那麼單點登入勢必是繞不過去的一個問題。作為前端程式員,我們對其雖然接觸不多,但适當的了解還是必要的。本文就來談談單點登入相關的問題。
前置知識
了解 SSO,最好具備以下知識。當然,如果不是特别熟,也不影響閱讀。
- cookie及session
- 浏覽器同源政策及跨域
- 了解登入系統的構成
什麼是 SSO 與 CAS?
SSO
SSO 是英文 Single Sign On 的縮寫,翻譯過來就是單點登入。顧名思義,它把兩個及以上個産品中的使用者登入邏輯抽離出來,達到隻輸入一次使用者名密碼,就能同時登入多個産品的效果。
打個比方,SSO 和我們去迪士尼玩時購買的通票很像。

我們隻要買一次通票,就可以玩所有遊樂場内的設施,而不需要在過山車或者摩天輪那裡重新買一次票。在這裡,買票就相當于登入認證,遊樂場就相當于使用一套 SSO 的公司,各種遊樂設施就相當于公司的各個産品。
使用 SSO 的優點很明顯:
- 提升使用者體驗。
就以我廠為例。我廠有兩個産品,丁香人才網和丁香園論壇,假如你是我廠使用者,肯定無法忍受登入丁香園論壇的時候輸入一次使用者名密碼,登入人才網又要輸入一次使用者名密碼吧?
- 避免重複開發
假如你是我廠後端,每天任務都飽和的不行,肯定無法忍受到人才網開發一套登入邏輯,到論壇又開發一套登入邏輯吧?
- 提升安全系數
假如你是我廠運維,發現了一個安全隐患需要緊急修複。你肯定無法忍受給茫茫多的産品後端都發一封郵件,責令修複吧?萬一漏了一個呢?
綜合看來,SSO 不僅是有用的,而且是必要的。
CAS
SSO 僅僅是一種架構,一種設計,而 CAS 則是實作 SSO 的一種手段。兩者是抽象與具體的關系。當然,除了 CAS 之外,實作 SSO 還有其他手段,比如簡單的 cookie。
CAS (Central Authentication Service)中心授權服務,本身是一個開源協定,分為 1.0 版本和 2.0 版本。1.0 稱為基礎模式,2.0稱為代理模式,适用于存在非 Web 應用之間的單點登入。本文隻涉及 CAS 1.0,下文中将詳細介紹。
SSO 的演進與分類
下面詳述一下各種場景下的 SSO,它們之間是逐漸更新,逐漸複雜化的關系。
1.同域 SSO
如圖,同域 SSO 是最簡單的一種情況。
此時,兩個産品都是在一個域名下,單點登入是很自然的選擇。我們來捋一捋步驟,搞清楚這裡的步驟是了解後文的基礎,千萬不要跳過。
- 使用者通路産品 a,向 背景伺服器發送登入請求。
- 登入認證成功,伺服器把使用者的登入資訊寫入 session。
- 伺服器為該使用者生成一個 cookie,并加入到 response header 中,随着請求傳回而寫入浏覽器。該 cookie 的域設定為 dxy.cn。
- 下一次,當使用者通路同域名的産品 b 時,由于 a 和 b 在同一域名下,也是 dxy.cn,浏覽器會自動帶上之前的 cookie。此時背景伺服器就可以通過該 cookie 來驗證登入狀态了。
實際上,這種場景就是最簡單最傳統的登入操作。雖然我們把産品 a 和 b 人為分開了,但由于它們在同域上,就算看成是同一産品的不同類目也未嘗不可。我們沒有設定獨立的 SSO 伺服器,因為業務背景伺服器本身就足以承擔 SSO 的職能。
[data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>](data:image/svg+xml;utf8,<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="800" height="600"></svg>)
2.同父域 SSO
同父域 SSO 是同域 SSO 的簡單更新,唯一的不同在于,伺服器在傳回 cookie 的時候,要把cookie 的 domain 設定為其父域。
比如兩個産品的位址分别為 a.dxy.cn 和 b.dxy.cn,那麼 cookie 的域設定為 dxy.cn 即可。在通路 a 和 b 時,這個 cookie 都能發送到伺服器,本質上和同域 SSO 沒有差別。
3.跨域 SSO
可以看到,在上面兩種情況下,我們都沒有專門設定 SSO 伺服器。但是當兩個産品不同域時,cookie 無法共享,是以我們必須設定獨立的 SSO 伺服器了。這個時候,我們就是通過标準的 CAS 方案來實作 SSO 的。下面我們就來詳細介紹一下:
詳解CAS
CAS 1.0 協定定義了一組術語,一組票據,一組接口。
術語:
- Client:使用者。
- Server:中心伺服器,也是 SSO 中負責單點登入的伺服器。
- Service:需要使用單點登入的各個服務,相當于上文中的産品 a/b。
接口:
- /login:登入接口,用于登入到中心伺服器。
- /logout:登出接口,用于從中心伺服器登出。
- /validate:用于驗證使用者是否登入中心伺服器。
- /serviceValidate:用于讓各個 service 驗證使用者是否登入中心伺服器。
票據
- TGT:Ticket Grangting Ticket
TGT 是 CAS 為使用者簽發的登入票據,擁有了 TGT,使用者就可以證明自己在 CAS 成功登入過。TGT 封裝了 Cookie 值以及此 Cookie 值對應的使用者資訊。當 HTTP 請求到來時,CAS 以此 Cookie 值(TGC)為 key 查詢緩存中有無 TGT ,如果有的話,則相信使用者已登入過。
- TGC:Ticket Granting Cookie
CAS Server 生成TGT放入自己的 Session 中,而 TGC 就是這個 Session 的唯一辨別(SessionId),以 Cookie 形式放到浏覽器端,是 CAS Server 用來明确使用者身份的憑證。
- ST:Service Ticket
ST 是 CAS 為使用者簽發的通路某一 service 的票據。使用者通路 service 時,service 發現使用者沒有 ST,則要求使用者去 CAS 擷取 ST。使用者向 CAS 發出擷取 ST 的請求,CAS 發現使用者有 TGT,則簽發一個 ST,傳回給使用者。使用者拿着 ST 去通路 service,service 拿 ST 去 CAS 驗證,驗證通過後,允許使用者通路資源。
票據之間的關系如下圖。注意,PGTIOU, PGT, PT 是 CAS 2.0 中的内容,感興趣的同學可以自行了解。
詳細步驟
看到這裡,是不是又有點暈了?沒關系,下面我們借助一個簡單的場景,再來仔細捋一捋用 CAS 實作 SSO 的詳細步驟,順便加深了解之前提出的概念。
開始!
- 使用者通路産品 a,域名是 www.a.cn。
- 由于使用者沒有攜帶在 a 伺服器上登入的 a cookie,是以 a 伺服器傳回 http 重定向,重定向的 url 是 SSO 伺服器的位址,同時 url 的 query 中通過參數指明登入成功後,回跳到 a 頁面。重定向的url 形如 sso.dxy.cn/login?service=https%3A%2F%2Fwww.a.cn。
- 由于使用者沒有攜帶在 SSO 伺服器上登入的 TGC(看上面,票據之一),是以 SSO 伺服器判斷使用者未登入,給使用者顯示統一登入界面。使用者在 SSO 的頁面上進行登入操作。
- 登入成功後,SSO 伺服器建構使用者在 SSO 登入的 TGT(又一個票據),同時傳回一個 http 重定向。這裡注意:
- 重定向位址為之前寫在 query 裡的 a 頁面。
- 重定向位址的 query 中包含 sso 伺服器派發的 ST。
- 重定向的 http response 中包含寫 cookie 的 header。這個 cookie 代表使用者在 SSO 中的登入狀态,它的值就是 TGC。
浏覽器重定向到産品 a。此時重定向的 url 中攜帶着 SSO 伺服器生成的 ST。根據 ST,a 伺服器向 SSO 伺服器發送請求,SSO 伺服器驗證票據的有效性。驗證成功後,a 伺服器知道使用者已經在 sso 登入了,于是 a 伺服器建構使用者登入 session,記為 a session。并将 cookie 寫入浏覽器。注意,此處的 cookie 和 session 儲存的是使用者在 a 伺服器的登入狀态,和 CAS 無關。之後使用者通路産品 b,域名是 www.b.cn。由于使用者沒有攜帶在 b 伺服器上登入的 b cookie,是以 b 伺服器傳回 http 重定向,重定向的 url 是 SSO 伺服器的位址,去詢問使用者在 SSO 中的登入狀态。浏覽器重定向到 SSO。注意,第 4 步中已經向浏覽器寫入了攜帶 TGC 的cookie,是以此時 SSO 伺服器可以拿到,根據 TGC 去查找 TGT,如果找到,就判斷使用者已經在 sso 登入過了。SSO 伺服器傳回一個重定向,重定向攜帶 ST。注意,這裡的 ST 和第4步中的 ST 是不一樣的,事實上,每次生成的 ST 都是不一樣的。浏覽器帶 ST 重定向到 b 伺服器,和第 5 步一樣。b 伺服器根據票據向 SSO 伺服器發送請求,票據驗證通過後,b 伺服器知道使用者已經在 sso 登入了,于是生成 b session,向浏覽器寫入 b cookie。