使用 Sa-Token 實現不同的登入模式:單地登入、多地登入、同端互斥登入

2023-07-10 09:01:28

一、需求分析

如果你經常使用騰訊QQ,就會發現它的登入有如下特點:它可以手機電腦同時線上,但是不能在兩個手機上同時登入一個賬號。

同端互斥登入,指的就是:像騰訊QQ一樣,在同一型別裝置上只允許單地點登入,在不同型別裝置上允許同時線上。

動態演示圖:

Sa-Token 是一個輕量級 java 許可權認證框架,主要解決登入認證、許可權認證、單點登入、OAuth2、微服務閘道器鑑權 等一系列許可權相關問題。
Gitee 開源地址:https://gitee.com/dromara/sa-token

本文將介紹在 Sa-Token 中,如何實現以下登入策略:

  • 單地登入:指一個賬號同一時間只能在一個地方登入,新登入會擠掉舊登入,也可以叫:單端登入。
  • 多地登入:指一個賬號同一時間可以在不同地方登入,新登入會和舊登入共存,也可以叫:多端登入。
  • 同端互斥登入:在同一型別裝置上只允許單地點登入,在不同型別裝置上允許同時線上,參考騰訊QQ的登入模式:手機和電腦可以同時線上,但不能兩個手機同時線上。

與之對應的,登出策略也將分為以下幾種:

  • 單端登出:只在呼叫退出的一端登出。
  • 全端登出:一端登出,全端下線。
  • 同端登出:例如將所有手機端登出下線,PC端不受影響。

二、多地登入

此模式較為簡單,Sa-Token 預設模式即為多地登入模式。

1、首先引入 Sa-Token 依賴:
<!-- Sa-Token 許可權認證 -->
<dependency>
	<groupId>cn.dev33</groupId>
	<artifactId>sa-token-spring-boot-starter</artifactId>
	<version>1.34.0</version>
</dependency>

注:如果你使用的是 SpringBoot 3.x,只需要將 sa-token-spring-boot-starter 修改為 sa-token-spring-boot3-starter 即可。

2、在使用者登入時將賬號id寫入對談中
@RestController
@RequestMapping("/user/")
public class UserController {
	@RequestMapping("doLogin")
	public SaResult doLogin(String username, String password) {
		// 此處僅作範例模擬,真實專案需要從資料庫中查詢資料進行比對 
		if("zhang".equals(username) && "123456".equals(password)) {
			StpUtil.login(10001);
			return SaResult.ok("登入成功");
		}
		return SaResult.ok("登入失敗");
	}
}

啟動類:

@SpringBootApplication
public class SaTokenDemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(SaTokenDemoApplication.class, args); 
		System.out.println("\n啟動成功:Sa-Roken 設定如下:" + SaManager.getConfig());
	}
}

如上程式碼,在多人登入同一賬號時將不會對舊對談做任何處理,同一賬號可以在多個地點任意登入,互不影響。

3、如果要全端登出,可以呼叫 logout 方法:
// 對談登出
@RequestMapping("logout")
public SaResult logout() {
	StpUtil.logout();
	return SaResult.ok("退出登入成功");
}

呼叫如上方法登出後,當前賬號所有端將一起下線。

4、單端登出

如果要只登出一端,可將組態檔中 is-share 的值設定為 false

sa-token:
	is-share: false

此設定項的含義為:在多人登入同一賬號時,是否共用一個 Token。

  • 為 true 時:所有登入共用一個 Token。
  • 為 false 時:每次登入新建一個 Token。

此值為 false 後,每次登入都將返回不同的 Token,與之對應的,呼叫 StpUtil.logout() 也只會登出掉當前的 Token,其他端不受影響。

三、單地登入

單地登入的重點是需要改一下 yml 組態檔:

sa-token: 
	is-concurrent: false

is-concurrent 的含義為是否允許同一賬號並行登入:

  • 為 true 時:允許一起登入。
  • 為 false 時:新登入擠掉舊登入。

其它程式碼與 [多地登入] 無異,當我們在兩個瀏覽器分別登入同一賬號時,舊對談再次存取系統將會得到如下提示:

{
	"code": 401,
	"msg": "Token 已被頂下線",
	"data": null
}

在 單地登入 模式中,不存在登出策略的問題,因為同一時間內,一個賬號最多在一個裝置線上,只要呼叫登出,就必然是全端下線。

四、同端互斥登入

好了,終於輪到主角出場,同端互斥登入可以讓我們像騰訊QQ一樣,在同一型別裝置上只允許單地點登入,在不同型別裝置上允許同時線上。

那麼在 Sa-Token 中如何做到同端互斥登入呢?

首先如 單地登入一樣,在組態檔中,將 sa-token.is-concurrent 設定為false,然後呼叫登入等相關介面時宣告裝置標識即可:

1、指定裝置標識登入
StpUtil.login(10001, "PC");    

呼叫此方法登入後,同裝置的會被頂下線(不同裝置不受影響),再次存取系統時會丟擲 NotLoginException 異常,場景值=-4

場景值 對應常數 含義說明
-1 NotLoginException.NOT_TOKEN 未能從請求中讀取到 Token
-2 NotLoginException.INVALID_TOKEN 已讀取到 Token,但是 Token無效
-3 NotLoginException.TOKEN_TIMEOUT 已讀取到 Token,但是 Token已經過期
-4 NotLoginException.BE_REPLACED 已讀取到 Token,但是 Token 已被頂下線
-5 NotLoginException.KICK_OUT 已讀取到 Token,但是 Token 已被踢下線

如果第二個引數填寫null或不填,代表將這個賬號id所有線上端踢下線,被踢出者再次存取系統時會丟擲 NotLoginException 異常,場景值=-5

2、查詢當前登入的裝置標識
StpUtil.getLoginDevice(); 

如果在登入時未指定裝置型別值,呼叫此方法將返回預設值:default-device

3、指定裝置端型別下線

業務場景舉例:在手機端控制PC端下線(手機端本身不受影響)

StpUtil.logout(10001, "PC");		
4、全端下線

在呼叫 logout 方法時,不填寫具體的裝置端型別,將預設控制所有端一起下線。

StpUtil.logout(10001);		

以上就是 Sa-Token 框架在處理登入問題時的各種方案,可以看出不管是簡單的多地登入還是複雜的同端互斥登入,在 Sa-Token 都有完善的解決方案。


參考資料