聊聊我在這家公司設計的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 | 返回的校驗憑證 |
該介面其實就需要幹兩件事情:
- 請求SSO進行token校驗(類似之前的使用者、密碼登入,只不過呼叫SSO平臺介面校驗);
- 建立本地會話(和賬號密碼登入成功之後的建立會話方式一致)。
- 控制校驗通過後的頁面跳轉。
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來看待