Java使用Sa-Token框架完成踢人下線功能
一、需求
在企業(yè)級項(xiàng)目中,踢人下線是一個很常見的需求,如果要設(shè)計(jì)比較完善的話,至少需要以下功能點(diǎn):
- 可以根據(jù)用戶 userId 踢出指定會話,對方再次訪問系統(tǒng)會被提示:您已被踢下線,請重新登錄。
- 可以查詢出一個賬號共在幾個設(shè)備端登錄,并返回其對應(yīng)的 Token 憑證,以便后續(xù)操作。
- 可以只踢出一個賬號某一個端的會話,其他端不受影響。例如在某電商APP上可以看到當(dāng)前賬號共在幾個手機(jī)上登錄,并注銷指定端的會話,當(dāng)前端不受影響。
手動從零開始設(shè)計(jì)滿足需求的會話架構(gòu),還是需要一定的代碼量的。本篇將介紹如何使用 Sa-Token 方便的完成上述需求,Sa-Token 框架對踢人下線做了較為完整的封裝,我們可以使用極少的代碼就完成踢人下線功能。
Sa-Token 是一個輕量級 java 權(quán)限認(rèn)證框架,主要解決登錄認(rèn)證、權(quán)限認(rèn)證、單點(diǎn)登錄、OAuth2、微服務(wù)網(wǎng)關(guān)鑒權(quán) 等一系列權(quán)限相關(guān)問題。Gitee 開源地址:gitee.com/dromara/sa-token
首先在項(xiàng)目中引入 Sa-Token 依賴:
<!-- Sa-Token 權(quán)限認(rèn)證 --> <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
即可。
二、踢人下線 API 一覽
先看看 Sa-Token 為我們提供的與踢人下線有關(guān)的API。
強(qiáng)制注銷:
StpUtil.logout(10001); // 強(qiáng)制指定賬號注銷下線 StpUtil.logout(10001, "PC"); // 強(qiáng)制指定賬號指定端注銷下線 StpUtil.logoutByTokenValue("token"); // 強(qiáng)制指定 Token 注銷下線
踢人下線:
StpUtil.kickout(10001); // 將指定賬號踢下線 StpUtil.kickout(10001, "PC"); // 將指定賬號指定端踢下線 StpUtil.kickoutByTokenValue("token"); // 將指定 Token 踢下線
強(qiáng)制注銷 和 踢人下線 的區(qū)別在于:
- 強(qiáng)制注銷等價于對方主動調(diào)用了注銷方法,再次訪問會提示:Token無效。
- 踢人下線不會清除Token信息,而是將其打上特定標(biāo)記,再次訪問會提示:Token已被踢下線。
動態(tài)圖演示:
下面開始進(jìn)行代碼實(shí)戰(zhàn)。
三、根據(jù)賬號踢人下線
在完成踢人下線之前,我們需要先讓會話完成登錄。正常的登錄需要根據(jù) username
+ password
判斷賬號合法性,由于我們本篇的重點(diǎn)是 踢人下線,所以此處簡化一下登錄操作,直接填入 userId 進(jìn)行登錄。
package com.pj; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.util.SaResult; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 測試踢人下線 */ @RestController @RequestMapping("/kick/") public class KickController { // 會話登錄接口 ---- http://localhost:8081/kick/doLogin?id=10001 @RequestMapping("doLogin") public SaResult doLogin(long userId) { StpUtil.login(userId); return SaResult.ok("登錄成功,Token 憑證為:" + StpUtil.getTokenValue()); } // 驗(yàn)證當(dāng)前客戶端是否登錄 ---- http://localhost:8081/kick/checkLogin @RequestMapping("checkLogin") public SaResult checkLogin() { StpUtil.checkLogin(); // 下面是登錄后才會返回的數(shù)據(jù) return SaResult.ok("您已登錄成功,userId=" + StpUtil.getLoginId()); } // 根據(jù)賬號Id踢人下線 ---- http://localhost:8081/kick/kickout @RequestMapping("kickout") public SaResult kickout(long userId) { StpUtil.kickout(userId); return SaResult.ok("將賬號 " + userId + " 踢下線成功"); } // 全局異常攔截 @ExceptionHandler public SaResult handlerException(Exception e) { e.printStackTrace(); return SaResult.error(e.getMessage()); } }
運(yùn)行代碼,分別用三個獨(dú)立的瀏覽器測試登錄:
// 使用瀏覽器 1 測試登錄賬號 10001 http://localhost:8081/kick/doLogin?userId=10001 // 使用瀏覽器 2 測試登錄賬號 10002 http://localhost:8081/kick/doLogin?userId=10002 // 使用瀏覽器 3 測試登錄賬號 10003 http://localhost:8081/kick/doLogin?userId=10003
之所以使用三個獨(dú)立的瀏覽器來測試,是為了避免會話的相互覆蓋,造成邏輯不可控。訪問成功的話,服務(wù)端的返回信息會類似如下:
{ "code": 200, "msg": "登錄成功,Token 憑證為:f53ac098-aed4-4de2-9223-8c3f1dab656d", "data": null }
然后使用三個瀏覽器分別訪問登錄驗(yàn)證接口:http://localhost:8081/kick/checkLogin復(fù)制代碼
返回信息如下:
{ "code": 200, "msg": "您已登錄成功,userId=10001", "data": null }
現(xiàn)在開始測試踢人下線,使用任意瀏覽器訪問:http://localhost:8081/kick/kickout?userId=10002復(fù)制代碼
返回信息:
{ "code": 200, "msg": "將賬號 10002 踢下線成功", "data": null }
賬號 10002 將被踢下線成功,現(xiàn)在我們再使用瀏覽器2 測試一下 10002 是否仍然在線:
{ "code": 500, "msg": "Token已被踢下線:aa5911a6-3623-4fdb-98d0-055c46353981", "data": null }
可以看到,10002會話已失效,無法通過登錄校驗(yàn)。
四、根據(jù) Token 踢人下線
業(yè)務(wù)場景舉例:我要在APP上查看我的賬號共在幾個設(shè)備登錄,并且將除我之外的設(shè)備全部踢下線。
首先我們需要在 application.yml
中添加配置:
sa-token: is-share: false
is-share 的含義是:在多人登錄同一賬號時,是否共用同一個 Token:
- 此值為 true 時,所有登錄共用一個Token。
- 此值為 false 時,每次登錄新建一個Token。
在以上 KickController
的基礎(chǔ)上,繼續(xù)添加接口:
/** * 測試踢人下線 */ @RestController @RequestMapping("/kick/") public class KickController { // 其他代碼... // 以下是需要新添加的代碼 // 查詢我的賬號已經(jīng)在幾個設(shè)備登錄 ---- http://localhost:8081/kick/tokenList @RequestMapping("tokenList") public SaResult tokenList() { long currUserId = StpUtil.getLoginIdAsLong(); List<String> tokenList = StpUtil.getTokenValueListByLoginId(currUserId); return SaResult.data(tokenList); } // 根據(jù) Token 踢人下線 ---- http://localhost:8081/kick/kickoutToken?token=xxxx @RequestMapping("kickoutToken") public SaResult kickoutToken(String token) { StpUtil.kickoutByTokenValue(token); return SaResult.ok("將Token: " + token + " 踢下線成功"); } }
重啟項(xiàng)目(如果集成 Redis 了就清空 Redis數(shù)據(jù)一下),分別從三個獨(dú)立的瀏覽器測試訪問:http://localhost:8081/kick/doLogin?userId=10001復(fù)制代碼
返回如下:
{ "code": 200, "msg": "登錄成功,Token 憑證為:450b8b73-f52d-4496-b67e-bdd579c8708a", "data": null }
仔細(xì)觀察三個瀏覽器返回的信息,雖然三個瀏覽器都是登錄賬號 10001,但是每次返回的 Token 憑證都是不一樣的。
現(xiàn)在查詢一下當(dāng)前賬號一共在幾個設(shè)備完成了登錄:http://localhost:8081/kick/tokenList復(fù)制代碼
返回如下:
{ "code": 200, "msg": "ok", "data": [ "450b8b73-f52d-4496-b67e-bdd579c8708a", "39d7974b-327d-4aea-a0b7-d90ab47caf0c", "d73c1bc5-d04f-4dc2-81ee-42c9438f9d78" ] }
現(xiàn)在選一個 Token,將其踢下線:http://localhost:8081/kick/kickoutToken?token=d73c1bc5-d04f-4dc2-81ee-42c9438f9d78復(fù)制代碼
返回信息如下:
{ "code": 200, "msg": "將Token: d73c1bc5-d04f-4dc2-81ee-42c9438f9d78 踢下線成功", "data": null }
然后在對應(yīng)的瀏覽器,驗(yàn)證一下登錄狀態(tài):http://localhost:8081/kick/checkLogin復(fù)制代碼
返回如下:
{ "code": 500, "msg": "Token已被踢下線:d73c1bc5-d04f-4dc2-81ee-42c9438f9d78", "data": null }
可以看到,該設(shè)備登錄的會話已被踢下線。那么同賬號的其他設(shè)備有沒有受到影響呢,我們從其他瀏覽器驗(yàn)證一下:http://localhost:8081/kick/checkLogin復(fù)制代碼
返回如下:
{ "code": 200, "msg": "您已登錄成功,userId=10001", "data": null }
可以看到,只有踢出的 Token 被強(qiáng)制下線了,其他端并沒有受到影響。
參考資料
- Sa-Token 文檔:sa-token.cc
- Gitee 倉庫地址:gitee.com/dromara/sa-token
- GitHub 倉庫地址:github.com/dromara/sa-token
到此這篇關(guān)于Java使用Sa-Token框架完成踢人下線功能的文章就介紹到這了,更多相關(guān)Sa-Token 踢人下線內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis 如何配置多個別名 typeAliasesPackage
這篇文章主要介紹了MyBatis 如何配置多個別名 typeAliasesPackage,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01借助Maven搭建Hadoop開發(fā)環(huán)境的最詳細(xì)教程分享
在Maven插件的幫助下,VSCode寫Java其實(shí)非常方便,所以本文就來和大家詳細(xì)講講如何借助maven用VScode搭建Hadoop開發(fā)環(huán)境,需要的可以參考下2023-05-05Spring?@EventListener?異步中使用condition的問題及處理
這篇文章主要介紹了Spring?@EventListener?異步中使用condition的問題及處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12