跨域分散式系統單點登入的實現(CAS單點登入)

追風人聊Java發表於2021-09-17

1. 概述

上一次我們聊了一下《使用Redis實現分散式會話》,原理就是使用 客戶端Cookie + Redis 的方式來驗證使用者是否登入。

如果分散式系統中,只是對Tomcat做了負載均衡,或者所有的子系統都在同一個二級域名下,則 客戶端Cookie + Redis 的方式是可以支援驗證使用者是否登入的。

如果分散式系統中包含了不同域名的子系統,之前的 客戶端Cookie + Redis 的方式就不支援了,因為二級域名不同,Cookie無法共享。

例如:瀏覽器在子系統A的二級域名中儲存了Cookie,在訪問子系統B時,無法從Cookie中拿到資料,因此沒有依據判斷使用者是否登入。

此時我們就需要引入CAS(中央認證服務),讓CAS幫助我們實現跨域單點登入。

今天我們就來聊聊這個跨域分散式系統單點登入的實現。

 

2. 場景說明

子系統A域名:www.a.com

子系統B域名:www.b.com

CAS系統域名:www.cas.com

當子系統A和子系統B都未登入,則客戶端請求時需要登入。

當客戶端請求子系統A觸發了登入,且登入成功,則客戶端再請求子系統B時,不需要登入,可直接訪問。

 

3.概念說明

全域性票據:一個全域性唯一的uuid,存放在域名為 .cas.com 的 Cookie 中,是使用者已登入的標識。

臨時票據:一個全域性唯一的uuid,存放在Redis中,有過期時限,用於驗證使用者的身份。

 

4. 跨域單點登入的實現邏輯

4.1 使用者訪問子系統A

使用者訪問子系統A的頁面,子系統A的頁面檢查引數中是否包含臨時票據,此時是沒有的,則什麼都不做。

子系統A的頁面請求子系統A的後臺介面,子系統A後臺檢查當前系統的Cookie中是否存在使用者ID。

因為從未登入過,所以Cookie中是不存在使用者ID的,因此子系統A後臺返回未登入給客戶端。

客戶端將當前子系統A頁面的 url 作為引數,重定向到CAS系統的登入頁面。

 

4.2 使用者在CAS系統登入

重定向到CAS系統的登入頁,登入頁首先判斷客戶端在CAS系統的Cookie中,是否存在全域性票據。

因為沒有登入過,所以在CAS系統的Cookie中不存在全域性票據,使用者需要填寫使用者名稱、密碼進行登入。

使用者在CAS系統的登入頁面,填寫使用者名稱、密碼後,提交登入。

CAS系統後臺,校驗使用者名稱、密碼,通過後,生成一個全域性唯一的 uuid,作為全域性票據。

將全域性票據儲存在域名為 .cas.com 的 Cookie 中。

以全域性票據為 key,使用者ID 為 value,存到 Redis 中。

以使用者ID為 key,使用者資訊為 value,存到 Redis 中。

再生成一個 uuid 作為臨時票據,以臨時票據為 key,臨時票據為 value,儲存到Redis中,5分鐘過期。

CAS系統返回臨時票據給客戶端。

客戶端將頁面重定向回子系統A的頁面,臨時票據作為引數攜帶。

 

4.3 再次訪問子系統A

頁面被重定向回子系統A的頁面。

子系統A的頁面檢查引數中是否有臨時票據,此時是有的,則子系統A的頁面Ajax呼叫CAS後臺的使用者身份驗證介面,以臨時票據作為引數。

CAS使用者身份驗證介面,以臨時票據為key,從redis中獲取value,value不為空,則驗證成功,然後清空Redis中的臨時票據。

從CAS系統的Cookie中得到全域性票據,以全域性票據為key,從Redis中得到使用者ID,返給子系統A的頁面。

子系統A的頁面拿到使用者ID後,儲存在域名為 .a.com 的 Cookie 中。

然後繼續完成業務介面邏輯。

 

子系統A再次訪問業務介面時,後臺檢查當前系統的Cookie中是否存在使用者ID,此時是存在的。

後臺使用使用者ID到Redis中獲取完整的使用者資訊,然後完成介面邏輯。

如果Redis中使用者資訊不存在,則表示使用者已登出登入,則返回未登入。

 

4.4 使用者訪問子系統B

使用者訪問子系統B的頁面,子系統B的頁面檢查引數中是否包含臨時票據,此時是沒有的,則什麼都不做。

子系統B的頁面呼叫子系統B的後臺介面,子系統B的後臺檢查當前系統的Cookie中是否存在使用者ID,不存在,返回未登入。

重定向到CAS系統的登入頁面,子系統B頁面的 url 作為引數。

CAS系統後臺,判斷在CAS系統的Cookie中,是否存在全域性票據,此時是存在的,因為使用的是同一個客戶端。

CAS系統後臺生成一個uuid作為臨時票據,以臨時票據為 key,臨時票據為 value,儲存到Redis中,5分鐘過期。

CAS系統返回臨時票據給客戶端。

客戶端將頁面重定向回子系統B的頁面,臨時票據作為引數攜帶。

 

子系統B的頁面檢查引數中是否有臨時票據,此時是有的,則子系統B的頁面Ajax呼叫CAS後臺的使用者身份驗證介面,以臨時票據作為引數。

CAS使用者身份驗證介面,以臨時票據為key,從redis中獲取value,value不為空,則驗證成功,然後清空Redis中的臨時票據。

從CAS系統的Cookie中得到全域性票據,以全域性票據為key,從Redis中得到使用者ID,返給子系統B的頁面。

子系統A的頁面拿到使用者ID後,儲存在域名為 .b.com 的 Cookie 中。

 

子系統B再次訪問業務介面時,後臺檢查當前系統的Cookie中是否存在使用者ID,此時是存在的。

後臺使用使用者ID到Redis中獲取完整的使用者資訊,然後完成介面邏輯。

 

這樣一來,使用者無感知的訪問了子系統B,不需要再次輸入使用者名稱、密碼去登入。

 

4.5 使用者登出

使用者登出時,首先清空當前子系統中使用者ID的Cookie。

然後子系統前端頁面Ajax訪問CAS系統的使用者登出介面。

CAS系統,從Cookie中拿到全域性票據,根據全域性票據從Redis中得到使用者ID。

刪除Cookie中的全域性票據。

從Redis中刪除 key 為 全域性票據 的資料。

從Redis中刪除 key 為 使用者ID 的資料 。

 

4.6 總結

這裡用了一個小技巧,a.com 和 b.com 的 Cookie 無法共享,因此就在一個公共的 cas.com 中去存Cookie,然後再做一次身份校驗就可以了。

 

5. 綜述

今天簡單聊了一下跨域分散式系統單點登入的實現,希望能對大家的工作有所幫助。

歡迎大家幫忙點贊、評論、加關注 :)

關注追風人聊Java,每天更新Java乾貨。

相關文章