如何設計一個單點登入系統(3)?

在上一篇文章

如何設計一個單點登入系統(2)?

中主要講解了可跨域SSO系統服務端,客戶端在登入,登出過程中分別應該承擔的職責,本文將重點聊一下具體技術實現,原始碼地址: https://github。com/zhoudapeng/zsso

首先聊服務端的實現,畢竟服務端是整個單點登入系統的大腦

提供登入頁,這個是登入的基礎,所有的接入方在發現當前使用者未登入的情況下都會重定向到sso服務端的登入頁,服務端的邏輯如下:

如何設計一個單點登入系統(3)?

sso服務端登入頁邏輯

這裡服務端需要做個判斷:

如果當前登入存在。sso。com域下的有效cookie,則證明此使用者之前在其他業務系統登入過,此時無需再讓使用者登入,只需要繫結token與此係統名的關係,然後重定向到此係統的回撥介面(接入方需要在此回撥介面中驗證token,建立本地會話)

不存在。sso。com域下的有效cookie,證明此使用者未登入過,需要跳轉到登入頁,讓使用者登入,為了使用者登入後能直接進入之前的頁面,這裡重定向的過程中需要帶上redirectUrl 這個引數

2。提供登入介面,使用者在登入頁輸入賬號密碼後會透過form表單已post的形式提交到此介面

如何設計一個單點登入系統(3)?

sso服務端登入form表單提交處理邏輯

如果賬號密碼不對,則再次跳轉到登入頁

賬號密碼匹配,則需要建立token,繫結token與系統名的關係,寫全域性cookie,然後重定向到接入方的回撥地址

3。提供驗證token介面

如何設計一個單點登入系統(3)?

sso服務端驗證token介面

token不存在,返回驗證失敗

token存在,則取出token的有效截止日期,並繫結token與系統名的關係

4。提供登出介面

如何設計一個單點登入系統(3)?

sso服務端登出介面

登出時,需要根據token查到所有繫結過的系統名,然後呼叫各個系統的登出介面一一登出。

以上是服務端需要提供的4個介面,下面我們再將一下客戶端的實現

在第一篇文章中講過,一個好的sso系統必須要讓接入方接入簡單,所以本人借鑑了spring auto-driven的思想,也提供了對應的名稱空間,並抽象出了UrlHelper,UserStore,ZssoClient,ZssoConfigResolver4個元件,以及預設實現類,接入方在接入過程中可以隨意拓展這4個元件,然後直接注入到業務層上下文即可。

UrlHelper:拼接url的元件,如無特殊需求不建議重新實現

ZssoClient:跟服務端互動的元件,如無特殊需求不建議重新實現

ZssoConfigResolver:解析配置檔案的元件,預設實現是讀取本地classpath下zsso-client。properties配置檔案,如無特殊需求不建議重新實現

UserStore:存取使用者資訊的元件,包括根據token解析使用者資訊,繫結token與userId關係,解綁token,預設實現只是為了演示用,接入方請務必自己實現此介面,並以userStore的名稱注入到業務層上下文中。

拓展元件使用方式:

如何設計一個單點登入系統(3)?

拓展UserStore元件配置方式

在spring掃描掉時會去回撥ZssoNamespaceHandler的init方法,最終執行ZssoBeanDefinitionParser的parse方法,在此方法中會去檢查每個元件是否有注入對應的BeanDefinition,如果沒有則會自動注入預設實現類,核心程式碼如下:

如何設計一個單點登入系統(3)?

ZssoBeanDefinitionParser核心程式碼

如何設計一個單點登入系統(3)?

ComponentInitializer核心程式碼

至此元件初始化的問題已經解決了,另外需要接入方開發的是如下問題:

判斷登入狀態

登入成功回撥介面(包括check token的邏輯)

登出回撥介面

既然每個接入方都有這樣的共性,所以我提供了一個ZssoFilter,在Filter里根據不同的url做不同的處理,接入方只需要配置此Filter即可,Filter內部會自動去獲取元件,接入方只需要拓展相關元件即可,ZssoFilter核心程式碼如下:

如何設計一個單點登入系統(3)?

ZssoFilter核心邏輯

LoginCallbackFilter已封裝好解析請求引數中token,check token,建立本地會話,跳轉到原頁面等功能

LoginCheckFilter已封裝好判斷當前使用者是否登入及跳轉到登入頁的邏輯

LogoutFilter已封裝好刪除本地會話的功能

程式碼中很多程式碼都是演示類的,尤其是server端的實現,比如保持使用者資訊都是存的本地快取,如果要放到生成環境則需要最佳化下這塊的邏輯,比如放到redis這樣的分散式快取等等。

最後非常感謝大家能在百忙中抽空看我的文章,大家的每一次檢視都是我繼續寫文章的動力,由於本人是典型理科男,文筆可能欠妥,希望大家能夠繼續支援我,我也會繼續努力,繼續堅持原創,但願能讓大家都能有所收穫!