1. 程式人生 > >聊聊我在這家公司設計的SSO

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

最近小明遇到一個需求:需要將幾個獨立的系統(子系統)彙總到一個集中的系統(父系統)當中,當用戶在父系統登入過後,再點選這幾個子系統,就可以免登入跳轉到任意一個系統。當時一聽,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跳轉一個子系統的時序圖:

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

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

SSO系統的登入與會話保持

本次會話管理採用的是redis session,spring完美支援redis儲存session資訊,此外還支援MONGODB\JDBC\HAZELCAST等儲存會話方式。通過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來看待