聊聊我在這家公司設計的SSO

  • 2019 年 10 月 3 日
  • 筆記

最近小明遇到一個需求:需要將幾個獨立的系統(子系統)匯總到一個集中的系統(父系統)當中,當用戶在父系統登錄過後,再點擊這幾個子系統,就可以免登錄跳轉到任意一個系統。當時一聽,duang~duang~就有很多方案湧進來(吹牛的),但只有下面這個方案得到了leader的肯定,如今已經在線上跑著了,接下來給大家復盤一下。

看完這個需求,大家是不是第一感覺就是:這不就是SSO(單點登錄)系統嘛?

單點登錄(英語:Single sign-on,縮寫為 SSO),又譯為單一簽入,一種對於許多相互關連,但是又是各自獨立的軟體系統,提供訪問控制的屬性。當擁有這項屬性時,當用戶登錄時,就可以獲取所有系統的訪問許可權,不用對每個單一系統都逐一登錄。這項功能通常是以輕型目錄訪問協議(LDAP)來實現,在伺服器上會將用戶資訊存儲到LDAP資料庫中。相同的,單一退出(single sign-off)就是指,只需要單一的退出動作,就可以結束對於多個系統的訪問許可權。

是的,沒錯,小明接到這個需求以後,整體思路也是按著SSO設想的,但是細想之後,發現不能完全照搬,要考慮項目的實際情況:比如已知的幾個子系統是之前的已經開發好的,不能大動干戈,需要平滑接入父系統,而且根據需求,SSO的功能也沒必要全部實現,簡而言之,就是一個閹割版的SSO。

小明只需要實現:用戶在父系統帳號密碼登錄後,通過點擊任意一個子系統的功能按鈕(不需要重複輸入帳號登錄)能夠跳轉子系統功能頁即可。

設計流程

項目

一個簡單樸素的SpringBoot項目

時序圖

說干就干,用戶輸入帳號密碼,請求SSO用戶登錄模組進行帳號密碼校驗,校驗通過後建立全局會話,並且返回前端token憑證(我使用的是sessionId),跳轉其他系統時攜帶token,其他系統拿到token後,再調用SSO平台進行token校驗,如果校驗通過,則用戶可在子系統內建立會話,用戶跳轉系統完成。下面給大家舉例SSO跳轉一個子系統的時序圖:

簡易版sso

在這裡插一嘴哈,我使用的流程圖工具是ProcessOn,是一款在線畫圖工具,非常適合畫各種示意圖,體驗極佳,如果大家想嘗試一下,可以使用我的邀請鏈接註冊使用~

整個流程圖如上面所示,下面主要針對各個功能點進行詳細說明。

SSO系統的登錄與會話保持

本次會話管理採用的是redis session,spring完美支援redis存儲session資訊,此外還支援MONGODBJDBCHAZELCAST等存儲會話方式。通過redis存儲session,可以滿足集群部署、分散式系統的session共享(當然這些都是後話)。

pom.xml依賴配置如下

        <!--redis 依賴-->          <dependency>              <groupId>org.springframework.boot</groupId>              <artifactId>spring-boot-starter-data-redis</artifactId>          </dependency>          <!--sessions 依賴-->          <dependency>              <groupId>org.springframework.session</groupId>              <artifactId>spring-session-data-redis</artifactId>          </dependency>

application.yml配置redis及session

spring:    redis:      host: 127.0.0.1      password: 123456      port: 6379      timeout: 1500      database: 0      jedis:        pool:          max-active: 1000          max-wait: -1          max-idle: 10          min-idle: 5    # 設置session存儲類型為redis    session:      store-type: redis

此時,redis存儲會話配置已經完成,但總覺得缺少什麼,嗷,原來除此之外,我們還需要設置session的有效時長,application.yml中的配置如下:

server:    servlet:      session:          # 支援Duration表達式,此時表示120分鐘        timeout: PT120M

當然,我們還需要在Springboot主方法通過@EnableRedisHttpSession開啟redis session

注意:如果上面配置的session有效時長不生效,我們可以在註解屬性上配置session有效時間(不瞞大家,我就是在此處配置才生效的)

@SpringBootApplication  // 開啟redis session ,並且設置session有效時長  @EnableRedisHttpSession(maxInactiveIntervalInSeconds = 7200)  public class XiaoMingApplication {        public static void main(String[] args) {          SpringApplication.run(XiaoMingApplication.class, args);      }  }

至此,sso系統的登錄及會話管理就完成啦。接下來看一下與其他系統如何交互。

跳轉子系統

流程

如果SSO已經登錄 -> 用戶點擊某個子系統按鈕(和負責A系統的人員約定好的鏈接)發起get請求 -> A系統後端接收到請求 -> 調用SSO系統進行token校驗(下面會講到) -> 建立會話,例如

http://xxx/jump?token=123456

這是一個在地址欄輸入的get請求,該介面需要特殊處理,後端攔截器需要放行。

參數說明
參數 說明
xxx 此處xxx為域名,jump為系統A提供的介面url地址,可以自定義,需要約定告知
token 返回的校驗憑證

該介面其實就需要干兩件事情:

  1. 請求SSO進行token校驗(類似之前的用戶、密碼登錄,只不過調用SSO平台介面校驗);
  2. 建立本地會話(和帳號密碼登錄成功之後的建立會話方式一致)。
  3. 控制校驗通過後的頁面跳轉。

SSO為子系統提供token校驗介面

上面講到SSO會暴漏一個token校驗介面,這一塊邏輯很簡單,就是拿著token去redis中查找對應的用戶資訊是否存在。眾所周知,小明是一個懶人,為了投機取巧,小明token的生成規則,就是session的id,因此,判斷用戶是否登錄,其實就是根據sessionId查找redis是否存在會話,此處有亮點。通過查看源碼,我發現這個功能,根本不用我們自己去實現,spring已經想到我們會用到。

spring提供了一個介面org.springframework.session.SessionRepository

package org.springframework.session;    public interface SessionRepository<S extends Session> {      S createSession();        void save(S var1);      // 這個就是      S findById(String var1);        void deleteById(String var1);  }

其中S findById(String var1)就是我們要調用的方法,這個方法作用就是根據sessionId去會話中心查找會話對象,正是我們所需要的,我們只需通過@Autowired獲取,開箱即用~我們一起看一下業務程式碼:

@Autowired  private SessionRepository sessionRepository;      @PostMapping("/checkToken")  public BaseResponseFacade checkToken(@RequestBody UserLoginVo userLoginVo) {      if (Objects.isNull(userLoginVo)) {          return ResponseUtil.error(NEED_LOGIN);      }      String token = userLoginVo.getToken();      Session session = sessionRepository.findById(token);      if (Objects.isNull(session)) {          return ResponseUtil.error(NEED_LOGIN);      }      AdverInfo adverInfo = JSON.parseObject(session.getAttribute("adverInfo"), AdverInfo.class);      return ResponseUtil.success(adverInfo);  }

SSO和子系統的交互文檔也貼出來給大家一睹為快

介面調用請求說明

  • 請求方式:POST
  • 請求格式:JSON
  • 請求地址
    • 測試:http:/xxx/checkToken
    • 正式:http://xxx/checkToken
  • POST數據示例
{      "token": "123456"  }
參數說明
參數 說明
xxx/checkToken xxx為域名,checkToken為營銷雲平台提供的校驗介面地址
token 調用介面憑據

返回說明

正常時返回的json數據包示例

如果SSO校驗通過,則系統A可以與與當前用戶建立本地會話,用戶正常進入系統

{      "data":{      "XXX":"XXX"      },      "errorMsg":"成功",      "errorCode":0  }

參數說明

參數 說明
errorCode 0:表示請求成功
errorMsg 返回碼說明
XXX 其他相關資訊
異常時返回的json數據包示例

當SSO後台校驗失敗時返回參數如下

{    "errorMsg": "NEED_LOGIN",    "errorCode": 10  }

參數說明

參數 說明
errorMsg 錯誤資訊說明
errorCode 錯誤標誌

錯誤碼說明

錯誤碼 說明
1 系統繁忙
10 需要用戶登錄
500 伺服器內部錯誤

小明設計的簡潔版sso就大抵如此,大家可以作為一個sso入門demo來看待?,如果大家有什麼建議或問題,歡迎留言~大家也可以關注微信公眾號「程式設計師小明」獲取更多資源~
歡迎關注微信公眾號」程式設計師小明」,獲取更多資源。