看圖理解JWT如何用於單點登陸

單點登陸是我比較喜歡的一個技術解決方案,一方面他可以提升產品使用的便利性,另外一方面他分離了各個應用都須要的登陸服務,對性能以及工做量都有好處。自從上次研究過JWT如何應用於會話管理,加之之前的項目中也一直在使用CAS這個比較流行的單點登陸框架,因此就一直在琢磨如何可以把JWT跟單點登陸結合起來一塊兒使用,儘可能能把兩種技術的優點都集成到項目中來。本文介紹我從CAS思考得出的SSO的實現方案。html

前言

其實CAS這個方案很好,很是強大,它最新的版本已經集成JWT了,因此要是不想本身開發單點登陸的服務的話,徹底能夠考慮使用CAS。可是我認爲,咱們在作項目的時候,也許一開始並不須要這麼強大的產品,CAS提供的登陸形式有不少,而咱們只須要應用其中的一種;並且它這個框架正是由於強大,因此也會比較複雜,簡單上手容易,可是遇到一些特殊的需求,好比咱們想在CAS裏面加入微信登陸,那就須要對它的原理以及API有比較深刻的瞭解才行。綜合考慮,仍是弄清楚CAS的原理,本身來實現一個基本的SSO服務比較放心。git

本文的內容須要對JWT和SSO有一個基本的瞭解,你能夠從這兩篇文章來了解JWT的用途:3種web會話管理的方式JWT實現token-based會話管理,還能夠從下面的資料來了解SSO的內容:SSO_百度百科github

方案介紹

本文主要是經過時序圖的方式來介紹JWT SSO的實現原理,具體的技術實現暫時尚未,不過當你理解了這個方案的原理後,你會以爲最終的實現並不會特別複雜,你能夠用任意的平臺語言來實現它。下面的時序圖,模擬了三個服務,分別是CAS、系統A、系統B,它們分別部署在cas.com,systemA.com和systemB.com;CAS這個服務用來管理SSO的會話;系統A和系統B表明着實際的業務系統。我從五個場景分別來講明這個SSO方案的實現細節。下面先來看第一個。web

場景一:用戶發起對業務系統的第一次訪問,假設他第一次訪問的是系統A的some/page這個頁面,它最終成功訪問到這個頁面的過程是:redis

sso_1

在這個過程裏面,我認爲理解的關鍵點在於:算法

1. 它用到了兩個cookie(jwt和sid)和三次重定向來完成會話的建立和會話的傳遞;緩存

1. jwt的cookie是寫在systemA.com這個域下的,因此每次重定向到systemA.com的時候,jwt這個cookie只要有就會帶過去;安全

2. sid的cookie是寫在cas.com這個域下的,因此每次重定向到cas.com的時候,sid這個cookie只要有就會帶過去;服務器

3. 在驗證jwt的時候,如何知道當前用戶已經建立了sso的會話?由於jwt的payload裏面存儲了以前建立的sso 會話的session id,因此當cas拿到jwt,就至關於拿到了session id,而後用這個session id去判斷有沒有的對應的session對象便可。微信

還要注意的是:CAS服務裏面的session屬於服務端建立的對象,因此要考慮session id惟一性以及session共享(假如CAS採用集羣部署的話)的問題。session id的惟一性能夠經過用戶名密碼加隨機數而後用hash算法如md5簡單處理;session共享,能夠用memcached或者redis這種專門的支持集羣部署的緩存服務器管理session來處理。

因爲服務端session具備生命週期的特色,到期需自動銷燬,因此不要本身去寫session的管理,省得引起其它問題,到github裏找開源的緩存管理中間件來處理便可。存儲session對象的時候,只要用session id做爲key,session對象自己做爲value,存入緩存便可。session對象裏面除了session id,還能夠存放登陸以後獲取的用戶信息等業務數據,方便業務系統調用的時候,從session裏面返回會話數據。

場景二:用戶登陸以後,繼續訪問系統A的其它頁面,如some/page2,它的處理過程是:

sso_2

從這一步能夠看出,即便登陸以後,也要每次跟CAS校驗jwt的有效性以及會話的有效性,其實jwt的有效性也能夠放在業務系統裏面處理的,可是會話的有效性就必須到CAS那邊才能完成了。當CAS拿到jwt裏面的session id以後,就能到session 緩存服務器裏面去驗證該session id對應的session對象是否存在,不存在,就說明會話已經銷燬了(退出)。

場景三:用戶登陸了系統A以後,再去訪問其餘系統如系統B的資源,好比系統B的some/page,它最終能訪問到系統B的some/page的流程是:

sso_3

這個過程的關鍵在於第一次重定向的時候,它會把sid這個cookie帶回給CAS服務器,因此CAS服務器可以判斷出會話是否已經創建,若是已經創建就跳過登陸頁的邏輯。

場景四:用戶繼續訪問系統B的其它資源,如系統B的some/page2:

sso_4

這個場景的邏輯跟場景二徹底一致。

場景五:退出登陸,假如它從系統B發起退出,最終的流程是:

sso_5

最重要的是要清除sid的cookie,jwt的cookie可能業務系統都有建立,因此不可能在退出的時候還挨個去清除那些系統的cookie,只要sid一清除,那麼即便那些jwt的cookie在下次訪問的時候還會被傳遞到業務系統的服務端,因爲jwt裏面的sid已經無效,因此最後仍是會被重定向到CAS登陸頁進行處理。

方案總結

以上方案兩個關鍵的前提:

1. 整個會話管理其實仍是基於服務端的session來作的,只不過這個session只存在於CAS服務裏面;

2. CAS之因此信任業務系統的jwt,是由於這個jwt是CAS簽發的,理論上只要認證經過,就能夠認爲這個jwt是合法的。

jwt自己是不可僞造,不可篡改的,可是不表明非法用戶冒充正經常使用法發起請求,因此常規的幾個安全策略在實際項目中都應該使用:

1. 使用https

2. 使用http-only的cookie,針對sid和jwt

3. 管理好密鑰

4. 防範CSRF攻擊。

尤爲是CSRF攻擊形式,不少都是鑽代碼的漏洞發生的,因此一旦出現CSRF漏洞,而且被人利用,那麼別人就能用得到的jwt,冒充正經常使用戶訪問全部業務系統,這個安全問題的後果仍是很嚴重的。考慮到這一點,爲了在即便有漏洞的狀況將損害減至最小,能夠在jwt裏面加入一個系統標識,添加一個驗證,只有傳過來的jwt內的系統標識與發起jwt驗證請求的服務一致的狀況下,才容許驗證經過。這樣的話,一個非法用戶拿到某個系統的jwt,就不能用來訪問其它業務系統了。

在業務系統跟CAS發起attach/validate請求的時候,也能夠在CAS端作些處理,由於這個請求,在一次SSO過程當中,一個系統只應該發一次,因此只要以前已經給這個系統簽發過jwt了,那麼後續 同一系統的attach/validate請求均可以忽略掉。

總的來講,這個方案的好處有:

1. 徹底分佈式,跨平臺,CAS以及業務系統都可採用不一樣的語言來開發;

2. 業務系統如系統A和系統B,可實現服務端無狀態

3. 假如是本身來實現,那麼能夠輕易的在CAS裏面集成用戶註冊服務以及第三方登陸服務,如微信登陸等。

它的缺陷是:

1. 第一次登陸某個系統,須要三次重定向(不過能夠優化成兩次);

2. 登陸後的後續請求,每次都須要跟CAS進行會話驗證,因此CAS的性能負載會比較大

3. 登錄後的後續請求,每次都跟CAS交互,也會增長請求響應時間,影響用戶體驗。

本文小結

本文從理論層面介紹告終合jwt來實現SSO的方案原理,但願它能幫助一些朋友更好的理解SSO以及它的實現方法。本文方案參考自CAS的實現流程,你能夠從下面這個資料瞭解CAS的單點登陸實現過程:

https://apereo.github.io/cas/4.1.x/protocol/CAS-Protocol.html

它的流程跟我這個差異不是特別大,可是從清晰層面來講,我寫的仍是要更明瞭一些,因此對比起來閱讀,可能理解會更透徹些。

另外,這個方案考慮地不必定很全面,因此要是您發現了其中的問題,還請您幫忙指正,很是感謝:)

相關文章
相關標籤/搜索