如何設計一個單點登錄系統(3)?
在上一篇文章
如何設計一個單點登錄系統(2)?
中主要講解了可跨域SSO系統服務端,客戶端在登錄,登出過程中分別應該承擔的職責,本文將重點聊一下具體技術實現,源碼地址: https://github.com/zhoudapeng/zsso
首先聊服務端的實現,畢竟服務端是整個單點登錄系統的大腦
-
提供登錄頁,這個是登錄的基礎,所有的接入方在發現當前用戶未登錄的情況下都會重定向到sso服務端的登錄頁,服務端的邏輯如下:
這裏服務端需要做個判斷:
-
如果當前登錄存在.sso.com域下的有效cookie,則證明此用戶之前在其他業務系統登錄過,此時無需再讓用戶登錄,只需要綁定token與此系統名的關系,然後重定向到此系統的回調接口(接入方需要在此回調接口中驗證token,創建本地會話)
-
不存在.sso.com域下的有效cookie,證明此用戶未登錄過,需要跳轉到登錄頁,讓用戶登錄,為了用戶登錄後能直接進入之前的頁面,這裏重定向的過程中需要帶上redirectUrl 這個參數
2.提供登錄接口,用戶在登錄頁輸入賬號密碼後會通過form表單已post的形式提交到此接口
-
如果賬號密碼不對,則再次跳轉到登錄頁
-
賬號密碼匹配,則需要創建token,綁定token與系統名的關系,寫全局cookie,然後重定向到接入方的回調地址
3.提供驗證token接口
-
token不存在,返回驗證失敗
-
token存在,則取出token的有效截止日期,並綁定token與系統名的關系
4.提供登出接口
登出時,需要根據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的名稱註入到業務層上下文中。
拓展組件使用方式:
在spring掃描掉<zsso:auto-driven/>時會去回調ZssoNamespaceHandler的init方法,最終執行ZssoBeanDefinitionParser的parse方法,在此方法中會去檢查每個組件是否有註入對應的BeanDefinition,如果沒有則會自動註入默認實現類,核心代碼如下:
至此組件初始化的問題已經解決了,另外需要接入方開發的是如下問題:
-
判斷登錄狀態
-
登錄成功回調接口(包括check token的邏輯)
-
登出回調接口
既然每個接入方都有這樣的共性,所以我提供了一個ZssoFilter,在Filter裏根據不同的url做不同的處理,接入方只需要配置此Filter即可,Filter內部會自動去獲取組件,接入方只需要拓展相關組件即可,ZssoFilter核心代碼如下:
LoginCallbackFilter已封裝好解析請求參數中token,check token,創建本地會話,跳轉到原頁面等功能
LoginCheckFilter已封裝好判斷當前用戶是否登錄及跳轉到登錄頁的邏輯
LogoutFilter已封裝好刪除本地會話的功能
代碼中很多代碼都是演示類的,尤其是server端的實現,比如保持用戶信息都是存的本地緩存,如果要放到生成環境則需要優化下這塊的邏輯,比如放到redis這樣的分布式緩存等等。
最後非常感謝大家能在百忙中抽空看我的文章,大家的每一次查看都是我繼續寫文章的動力,由於本人是典型理科男,文筆可能欠妥,希望大家能夠繼續支持我,我也會繼續努力,繼續堅持原創,但願能讓大家都能有所收獲!
如何設計一個單點登錄系統(3)?