這一篇是原理篇,接下來還會有一篇實戰篇,實戰的相關程式碼是非常火的一個開源專案叫:xxl-sso
一、簡介
單點登入(Single Sign On),簡稱為 SSO。
它的解釋是在多個應用系統中,使用者只需要登入一次就可以訪問所有相互信任的應用系統。
所謂一次登入,處處登入。同樣一處退出,處處退出
。
二、背景
在我們企業發展初期的時候,企業內部使用的系統都會比較少,一般也就一個或者兩個,每個系統有自己的登入功能。運營人員將自己的賬號登入還是很方便。
但是隨著公司的發展,公司的系統越來越多,比如有OA系統、CRM系統、財務管理系統、裝置管理系統等,這個時候總不能每個系統都登入一遍吧,那真的會崩潰的。
合理做法是使用者只需要登入一次就可以訪問所有相互信任的應用系統。
三、回顧下單系統登入是怎麼樣的?
我們都知道,http是無狀態的協議,這意味著當你登入成功後請求其它介面服務端也並不知道你之前登入過。那怎麼辦呢?
這個時候我們會想到Cookie
+Session
組合來解決http無狀態問題。
如果說Cookie
是檢查使用者身上的”通行證“來確認使用者的身份,那麼Session
就是透過檢查伺服器上的”客戶明細表“來確認使用者的身份的。
那這裡完整的登入流程應該是這樣的:
1)、 首次登入驗證成功之後,後端會將使用者資訊存在Session物件中。
2)、同時設定 Set-Cookie 欄位,並把 SessionId 等資訊寫入進去,並設定過期時間,這些資訊就是 Cookie。瀏覽器會儲存這些 Cookie 資訊
3)、之後在請求該系統其它介面的時候,因為是同域名,瀏覽器會自動在請求頭上新增 Cookie 欄位,並帶上儲存的 Cookie 資訊。
4)、後端接受到請求後,會在請求頭中取出 SessionId的值,然後再去伺服器上的Session獲取對於的使用者資訊,如果獲取成功說明登入驗證成功了,不需要再重複登入。
總結
根據以上流程可知,SessionID 是連線 Cookie 和 Session 的一道橋樑,大部分系統也是根據此原理來驗證使用者登入狀態。
所以,一般我們單系統實現登入會這樣做:
-
登入:將使用者資訊儲存在Session物件中
-
- 如果在Session物件中能查到,說明已經登入
- 如果在Session物件中查不到,說明沒登入(或者已經退出了登入)
-
登出(退出登入):從Session中刪除使用者的資訊
四、多系統登入會存在的一些問題?
我們說單系統中登入流程實現關鍵點在於 Cookie
和 Session
的配合使用,但在多系統中就會存在很明顯的兩個問題
- 在多系統情況下服務端
Session
不共享。 - 在多系統情況下客戶端
Cookie
不共享(跨域)。
如果能解決這兩大難點,那實現多系統登入就簡單多了
1.為什麼會存在Session不共享?
我們說Session是儲存在服務端
的。
比如說現在有3臺Tomcat伺服器,當我們訪問第1臺Tomcat時,我們是可以將使用者資訊存在第1臺Tomcat的Session中,但當我們訪問第2臺Tomcat的時候,這臺伺服器是
沒有對應的Session資料,這就是所謂的Session不共享問題。
2、如何解決session共享問題呢?
說如何解決session共享問題呢,其實就是如何解決服務端資料共享問題
我們常見有3種解決方案:
第一種方案就是session複製
。
當某一臺Tomcat對session中的資訊進行了修改都會同步給其他Tomcat,這樣session就可以共享。
這種方案有三大缺陷
- 每一臺伺服器都會儲存一份完整的session,增加伺服器端壓力也會浪費記憶體。
- 因為涉及到服務之間的同步,所以可能存在延遲。
第二種方案就是不透過session共享資料,而是採用redis
。
redis純天然解決了session不能共享的問題,而且redis除了儲存查詢效率高以外,還支援資料持久化功能,不用擔心資料會丟失。
第二種方案也是現在現在企業級使用最多的一種方案。
第三種採用JWT
。
我們在使用session或者使用redis,前端cookie其實只是存了個key,我們還需要拿著這個key到服務端的session,或者redis或者Mysql,總之都需要查一遍,但如果是JWT,
它最大的特點就是這個JWT本身就含有使用者資訊,服務端只要解析這個JWT成功,就可以獲取使用者資訊。
3、為什麼會Cookie跨域問題?
本質:由於瀏覽器安全策略,cookie只能在同一域名產生和使用
比方說,我們在請求www.a.com的時候,瀏覽器會自動把www.a.com的Cookie帶去服務端。
但我們在請求www.b.com的時候,是不會把www.a.com下的Cookie帶到b伺服器的。
這就意味著由於域名不同,使用者向系統A登入後,系統A返回給瀏覽器的Cookie,使用者再請求系統B的時候不會將系統A的Cookie帶過去。
至於如何解決Cookie跨域問題,不在這篇文章的討論範疇內,下面實現單點登入的方式也不是透過解決Cookie跨域來實現的。
五、單點登入原理
相比於單系統登入,sso需要一個獨立的認證中心,只有認證中心能接受使用者的使用者名稱密碼等安全資訊,其他系統不提供登入入口,只接受認證中心的間接授權。
1、SSO應用核心設計
應用系統
:OA系統、CRM系統(需要登入的系統)
SSO客戶端
:登入、退出(獨立jar包給應用系統引用)
SSO服務端
:登入(登入服務)、登入狀態(提供登入狀態校驗/登入資訊查詢的服務)、退出(使用者登出服務)
資料庫
:儲存使用者賬戶資訊(一般使用Mysql)
快取
:儲存使用者的登入資訊(一般使用Redis)
2、SSO登入流程
對於這個流程圖,我看網上問的最多的一個問題就是
根據同源策略:只要 協議+域名+埠號 一個不同,那麼就不能進行跨域。www.oa.com 和 www.crm.com 域名都不相同了。也就是www.crm.com是
拿不到www.oa.com中cookie中的token的,那crm.com在請求的時候為什麼不需要登入呢?
其實這個問題,上面的流程圖已經很清楚了。它也並不是透過解決跨域問題來實現單點登入的。
它實現的核心原理在於:
個人使用者請求www.oa.com時,因為oa.com的cookie下沒有token資訊,所以跳轉到sso.com/login,因為是第一次登入,所以sso.com的cookie下也沒有token資訊,所以需要
使用者輸入賬號密碼登入,登入成功會在sso.com域名下儲存token資訊,同時會把token資訊返回給oa.com。
這樣oa.com和sso.com下的cookie都有token資訊。
而第一次訪問crm.com的時候,它下面是沒有token資訊,所以會跳轉到sso.com/login進行登入,但因為sso.com域名下cookie已經有token資訊,所以不用再輸入賬號密碼資訊
直接把token返回到crm.com就可以,這個過程使用者是無感知的,所以也就實現了一次登入處處登入了。
3、sso登出流程
對於這個流程,問的比較多的是: oa.com退出登入了。如何做到讓crm.com也需要重新登入的?
透過上面的流程圖我們可以知道www.oa.com退出登入,只能去除oa.com
和sso.com
域名下cookie下的token,但是crm.com域名下的cookie還是可以獲取token的,
那能獲取就代表這可以正常訪問www.crm.com的介面了嗎?
其實不是的,因為我們還有校驗token有效性這一步(令牌校驗),我們拿著這個token去redis獲取使用者資訊,其實已經獲取不到了,因為上面退出登入的時候已經清除了,
所以令牌校驗失敗一樣要重新登入。
宣告: 公眾號如需轉載該篇文章,發表文章的頭部一定要 告知是轉至公眾號: 後端元宇宙。同時也可以問本人要markdown原稿和原圖片。其它情況一律禁止轉載!