一、需求
在企业级项目中,踢人下线是一个很常见的需求,假如要规划比较完善的话,至少需求以下功能点:
- 能够依据用户 userId 踢出指定会话,对方再次拜访体系会被提示:您已被踢下线,请从头登录。
- 能够查询出一个账号共在几个设备端登录,并回来其对应的 Token 凭据,以便后续操作。
- 能够只踢出一个账号某一个端的会话,其他端不受影响。例如在某电商APP上能够看到当时账号共在几个手机上登录,并刊出指定端的会话,当时端不受影响。
手动从零开端规划满意需求的会话架构,仍是需求一定的代码量的。本篇将介绍如何运用 Sa-Token 便利的完结上述需求, Sa-Token 结构对踢人下线做了较为完整的封装,咱们能够运用很少的代码就完结踢人下线功能。
Sa-Token 是一个轻量级 java 权限认证结构,主要处理登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。 Gitee 开源地址:gitee.com/dromara/sa-…
首要在项目中引进 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
即可。
二、踢人下线 API 一览
先看看 Sa-Token 为咱们供给的与踢人下线有关的API。
强制刊出:
StpUtil.logout(10001); // 强制指定账号刊出下线
StpUtil.logout(10001, "PC"); // 强制指定账号指定端刊出下线
StpUtil.logoutByTokenValue("token"); // 强制指定 Token 刊出下线
踢人下线:
StpUtil.kickout(10001); // 将指定账号踢下线
StpUtil.kickout(10001, "PC"); // 将指定账号指定端踢下线
StpUtil.kickoutByTokenValue("token"); // 将指定 Token 踢下线
强制刊出 和 踢人下线 的差异在于:
- 强制刊出等价于对方自动调用了刊出办法,再次拜访会提示:Token无效。
- 踢人下线不会清除Token信息,而是将其打上特定符号,再次拜访会提示:Token已被踢下线。
动态图演示:
下面开端进行代码实战。
三、依据账号踢人下线
在完结踢人下线之前,咱们需求先让会话完结登录。正常的登录需求依据 username
+ password
判别账号合法性,因为咱们本篇的重点是 踢人下线,
所以此处简化一下登录操作,直接填入 userId 进行登录。
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());
}
// 验证当时客户端是否登录 ---- http://localhost:8081/kick/checkLogin
@RequestMapping("checkLogin")
public SaResult checkLogin() {
StpUtil.checkLogin();
// 下面是登录后才会回来的数据
return SaResult.ok("您已登录成功,userId=" + StpUtil.getLoginId());
}
// 依据账号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());
}
}
运行代码,别离用三个独立的浏览器测验登录:
// 运用浏览器 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
之所以运用三个独立的浏览器来测验,是为了避免会话的相互覆盖,造成逻辑不可控。拜访成功的话,服务端的回来信息会相似如下:
{
"code": 200,
"msg": "登录成功,Token 凭据为:f53ac098-aed4-4de2-9223-8c3f1dab656d",
"data": null
}
然后运用三个浏览器别离拜访登录验证接口:
http://localhost:8081/kick/checkLogin
回来信息如下:
{
"code": 200,
"msg": "您已登录成功,userId=10001",
"data": null
}
现在开端测验踢人下线,运用恣意浏览器拜访:
http://localhost:8081/kick/kickout?userId=10002
回来信息:
{
"code": 200,
"msg": "将账号 10002 踢下线成功",
"data": null
}
账号 10002 将被踢下线成功,现在咱们再运用浏览器2 测验一下 10002 是否仍然在线:
{
"code": 500,
"msg": "Token已被踢下线:aa5911a6-3623-4fdb-98d0-055c46353981",
"data": null
}
能够看到,10002会话已失效,无法经过登录校验。
四、依据 Token 踢人下线
业务场景举例:我要在APP上查看我的账号共在几个设备登录,并且将除我之外的设备全部踢下线。
首要咱们需求在 application.yml
中增加装备:
sa-token:
is-share: false
is-share 的含义是:在多人登录同一账号时,是否共用同一个 Token:
- 此值为 true 时,所有登录共用一个Token。
- 此值为 false 时,每次登录新建一个Token。
在以上 KickController
的基础上,持续增加接口:
/**
* 测验踢人下线
*/
@RestController
@RequestMapping("/kick/")
public class KickController {
// 其他代码...
// 以下是需求新增加的代码
// 查询我的账号已经在几个设备登录 ---- http://localhost:8081/kick/tokenList
@RequestMapping("tokenList")
public SaResult tokenList() {
long currUserId = StpUtil.getLoginIdAsLong();
List<String> tokenList = StpUtil.getTokenValueListByLoginId(currUserId);
return SaResult.data(tokenList);
}
// 依据 Token 踢人下线 ---- http://localhost:8081/kick/kickoutToken?token=xxxx
@RequestMapping("kickoutToken")
public SaResult kickoutToken(String token) {
StpUtil.kickoutByTokenValue(token);
return SaResult.ok("将Token: " + token + " 踢下线成功");
}
}
重启项目(假如集成 Redis 了就清空 Redis数据一下),别离从三个独立的浏览器测验拜访:
http://localhost:8081/kick/doLogin?userId=10001
回来如下:
{
"code": 200,
"msg": "登录成功,Token 凭据为:450b8b73-f52d-4496-b67e-bdd579c8708a",
"data": null
}
仔细观察三个浏览器回来的信息,尽管三个浏览器都是登录账号 10001,但是每次回来的 Token 凭据都是不一样的。
现在查询一下当时账号一共在几个设备完结了登录:
http://localhost:8081/kick/tokenList
回来如下:
{
"code": 200,
"msg": "ok",
"data": [
"450b8b73-f52d-4496-b67e-bdd579c8708a",
"39d7974b-327d-4aea-a0b7-d90ab47caf0c",
"d73c1bc5-d04f-4dc2-81ee-42c9438f9d78"
]
}
现在选一个 Token,将其踢下线:
http://localhost:8081/kick/kickoutToken?token=d73c1bc5-d04f-4dc2-81ee-42c9438f9d78
回来信息如下:
{
"code": 200,
"msg": "将Token: d73c1bc5-d04f-4dc2-81ee-42c9438f9d78 踢下线成功",
"data": null
}
然后在对应的浏览器,验证一下登录状况:
http://localhost:8081/kick/checkLogin
回来如下:
{
"code": 500,
"msg": "Token已被踢下线:d73c1bc5-d04f-4dc2-81ee-42c9438f9d78",
"data": null
}
能够看到,该设备登录的会话已被踢下线。那么同账号的其他设备有没有受到影响呢,咱们从其他浏览器验证一下:
http://localhost:8081/kick/checkLogin
回来如下:
{
"code": 200,
"msg": "您已登录成功,userId=10001",
"data": null
}
能够看到,只有踢出的 Token 被强制下线了,其他端并没有受到影响。
参考资料
- Sa-Token 文档:sa-token.cc
- Gitee 库房地址:gitee.com/dromara/sa-…
- GitHub 库房地址:github.com/dromara/sa-…