天天看點

jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

最近我們組要給負責的一個管理系統 A 內建另外一個系統 B,為了讓使用者使用更加便捷,避免多個系統重複登入,希望能夠達到這樣的效果——使用者隻需登入一次就能夠在這兩個系統中進行操作。很明顯這就是**單點登入(Single Sign-On)**達到的效果,正好可以明目張膽的學一波單點登入知識。

本篇主要内容如下:

  • SSO 介紹
  • SSO 的幾種實作方式對比
  • 基于 JWT 的 spring boot 單點登入實戰

注意: SSO 這個概念已經出現很久很久了,目前各種平台都有非常成熟的實作,比如OpenSSO,OpenAM,Kerberos,CAS等,當然很多時候成熟意味着複雜。本文不讨論那些成熟方案的使用,也不考慮 SSO 在 CS 應用中的使用。

什麼是 SSO

單點點說就是:一次登入後可免登陸通路其他的可信平台。比如我們登入淘寶網後,再打開天貓首頁可以發現已經是登入狀态了。SSO 是一種比較流行的服務于企業業務整合的一種解決方案。

如何實作 SSO

我們都知道目前的 http 協定是無狀态的,也就是第一次請求和第二次請求是完全獨立,不相關的,但現實中我們的業務邏輯都是有狀态的,這樣就引入了 cookie-session 的機制來維護狀态,浏覽器端存儲一個 sessionId,背景存儲跟該 sessionId 相關的資料。每次向背景發起請求時都攜帶此 sessionId 就能維持狀态了。然後就有了 cookie,浏覽器在發送請求時自動将 cookie 中的資料放到請求中,發給服務端,無需手動設定。

然後我們可以考慮考慮實作 SSO 的核心是什麼?答案就是如何讓一個平台 A 登入後,其他的平台也能擷取到平台 A 的登入資訊(在 cookie-session 機制中就是 sessionId)。

方案一 共享 cookie

基于 cookie-session 機制的系統中,登入系統後會傳回一個 sessionId 存儲在 cookie 中,如果我們能夠讓另外一個系統也能擷取到這個 cookie,不就擷取到憑證資訊了,無需再次登入。剛好浏覽器的 cookie 可以實作這樣的效果(詳見web 跨域及 cookie 學習)。

cookie 允許同域名(或者父子域名)的不同端口中共享 cookie,這點和 http 的同域政策不一樣(http 請求隻要協定、域名、端口不完全相同便認為跨域)。是以隻需将多個應用前台頁面部署到相同的域名(或者父子域名),然後共享 session 便能夠實作單點登入。架構如下:

jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

上面方案顯而易見的限制就是不僅前台頁面需要共享 cookie,背景也需要共享 session(可以用jwt來幹掉 session,但是又會引入新的問題,這裡不展開).這個方案太簡單了,不作進一步說明。

方案二 基于回調實作

通過上文可以知道,要實作單點登入隻需将使用者的身份憑證共享給各個系統,讓背景知道現在是誰在通路。就能實作一次登入,到處通路的效果,實在是非常友善的。在 session 機制中是共享 sessionId,然後多個背景使用同一個 session 源即可。這裡我們用一種新的基于 JWT 的 token 方式來實作,不了解 JWT 的可以看這篇:java-jwt 生成與校驗,簡單來說 jwt 可以攜帶無法篡改的資訊(一段篡改就會校驗失敗),是以我們可以将使用者 id 等非敏感資訊直接放到 jwt 中,幹掉了背景的 session。然後我們要做的就是将 jwt 共享給各個平台頁面即可。系統架構如下:

jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

此架構中,業務系統 A 和業務系統 B 之間不需要有任何聯系,他們都隻和 SSO 認證平台打交道,是以可以任意部署,沒有同域的限制。你可能就要問了這樣要怎麼共享身份憑證(也就是 jwt 字元串)?這裡就要通過 url 參數來進行騷操作了。文字總結來說是這樣的:jwt 存到認證平台前端的 localStore(不一定是 localStore,cookie,sessionStore 都可以),然後業務平台攜帶自己的回調位址跳轉到認證中心的前台,認證中心的前台再将 ujwt 作為 url 參數,跳回到那個回調位址上,這樣就完成了 jwt 的共享。

文字很可能看不懂,下面是整個過程的路程圖:

jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

相信通過上面的流程圖你應該能大概看明白,jwt 是如何共享了的吧,還看不懂的繼續看下來,下面上一個 spring boot 實作的簡易 SSO 認證。主要有兩個系統:SSO 認證中心,系統 A(系統 A 換不同端口運作就是系統 A、B、C、D 了).

實戰

實作 SSO 認證中心

spring boot 架構先搭起來,由于是簡易項目,除 spring boot web 基本依賴,隻需要如下的額外依賴:

org.springframework.boot spring-boot-starter-data-rediscom.alibaba fastjson 1.2.4com.auth0 java-jwt 3.7.0複制代碼
           

完整的 POM 檔案,請到 github 上檢視.

背景實作

背景做的事情并不多,隻有以下 5 個方法:

  • /login : 登入成功後簽發一個 jwt token
  • 在 demo 中隻是簡單對比使用者名密碼如果是一樣的認為登入成功,傳回 token
  • /checkJwt : 檢查 jwt 的有效性
  • 檢查傳來的 jwt-token 是否有效,傳回失效的 jwt 清單
  • /refreshjwt : 重新整理 jwt
  • 判斷該 jwt 是否快要過期,如果快要過期,生成一個新的 jwt 傳回
  • /inValid : 讓某個 jwt 失效
  • jwt 如何失效一直是一個比較麻煩的問題,各有利弊。本例中采用的是為每個 jwt 生成一個随機的秘鑰 secret,将 jwt--secret 儲存到 redis 中,想要讓某個 jwt 失效,隻需将該記錄在 redis 中删除即可(這樣在解密時便無法擷取到 secret)。但是這樣讓無狀态的認證機制變成有狀态了(記錄了 jwt 和 secret 的對應關系)。

總結來說 SSO 背景主要隻做了兩件事:驗證使用者名密碼傳回 jwt;驗證 jwt 是否合法。具體代碼檢視 github 上 sso 目錄下的代碼。

前台實作

前台的邏輯較為複雜,不是那麼容易了解,不明白的多看幾遍上面的流程圖。

再次回到 SSO 的重點:分享登入狀态。要如何在前台将登入狀态(在這裡就是 jwt 字元串)分享出去呢?由于浏覽器的限制,除了 cookie 外沒有直接共享資料的辦法。既然沒有直接共享,那肯定是有間接的辦法的!

這個辦法就是回調。系統 A 的前台在跳轉到 SSO 的前台時,将目前路徑作為 url 參數傳遞給 sso 前台,sso 前台在擷取到 jwt 後,再跳轉到系統 A 傳過來的 url 路徑上,并帶上 jwt 作為 url 參數。這就完成了 jwt 的一次共享,從 sso 共享到系統 A。

打個比方:你點了個外賣,别人要怎麼把外賣給你呢?顯然你會留下的位址,讓别人帶上飯送到這個位址,然後你就能享用美食了。這和 jwt 的傳遞非常相識了。

系統 A 說:我要 jwt,快把它送到http://localhost:8081/test1/這個位址上。

SSO 說:好嘞,這個位址是合法的可以送 jwt 過去,這就跳轉過去:http://localhost:8081/test1/?jwt=abcdefj.asdf.asdfasf

系統 A 說:不錯不錯,真香。

要注意這裡有個坑就是:如果另外一個惡意系統 C 安裝相同的格式跳轉到 SSO,想要擷取 jwt,這顯然是不應該給它的。是以在回跳回去的時候要判斷一下這個回調位址是不是合法的,能不能給 jwt 給它,可以向背景請求判斷也可以在 sso 前台直接寫死合法的位址。在 demo 是沒有這個判斷過程的。

實作業務系統

業務系統代碼非常簡單,主要是用了一個攔截器,攔截 http 請求,提取出 token 向 sso 認證中心驗證 token 是否有效,有效放行,否則傳回錯誤給前端。太簡單也不貼代碼了,到 github 上看看就明白了。

效果

上面說了一大串都是原理了,其實這個難也就難在原理部分,代碼實作并沒有那麼複雜。這裡就不貼代碼了,有需要直接到 github 上看。

這裡上幾個效果圖:

  • 系統 A 首次登陸系統
jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

可以看到首次登陸是需要跳到 sso 認證中心輸入使用者名密碼進行登陸驗證的。登陸成功回跳後接口請求成功。

  • 将 A 的啟動端口改為 8082 後再次啟動,當作系統 B
jwt認證機制優勢和原理_手把手教你學會 基于Spring-Boot,JWT的單點登入什麼是 SSO如何實作 SSO效果

可以看到這次是無需登陸的,跳到認證中心後就馬上跳回了,如果去掉 alert 一般是看不出跳轉過程的。

最後在任意一個系統登出,都會讓所有的系統推出登陸。

可以看到,在系統 A 登入系統後,系統 B,系統 C 都不再需要輸入使用者名密碼進行登入。如果速度足夠快甚至都注意不到調到 SSO 再跳回來的過程。

繼續閱讀