使用 Sa-Token 完成踢人下线功能

一、需求

在企业级项目中,踢人下线是一个很常见的需求,如果要设计比较完善的话,至少需要以下功能点:

  • 可以根据用户 userId 踢出指定会话,对方再次访问系统会被提示:您已被踢下线,请重新登录。
  • 可以查询出一个账号共在几个设备端登录,并返回其对应的 Token 凭证,以便后续操作。
  • 可以只踢出一个账号某一个端的会话,其他端不受影响。例如在某电商APP上可以看到当前账号共在几个手机上登录,并注销指定端的会话,当前端不受影响。

手动从零开始设计满足需求的会话架构,还是需要一定的代码量的。本篇将介绍如何使用 Sa-Token 方便的完成上述需求,
Sa-Token 框架对踢人下线做了较为完整的封装,我们可以使用极少的代码就完成踢人下线功能。

Sa-Token 是一个轻量级 java 权限认证框架,主要解决登录认证、权限认证、单点登录、OAuth2、微服务网关鉴权 等一系列权限相关问题。
Gitee 开源地址:https://gitee.com/dromara/sa-token

首先在项目中引入 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 被强制下线了,其他端并没有受到影响。


参考资料

原文链接:https://www.cnblogs.com/shengzhang/p/17384468.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:使用 Sa-Token 完成踢人下线功能 - Python技术站

(0)
上一篇 2023年5月9日
下一篇 2023年5月9日

相关文章

  • SpringMVC整合,出现注解没有起作用的情况处理

    对于SpringMVC整合过程中注解没有起作用的情况,处理方法如下: 确认是否开启注解扫描 在 SpringMVC 的配置文件中,需要先配置注解扫描,才能让 SpringMVC 自动识别使用了注解的类和方法。配置示例如下: <context:component-scan base-package="com.example.controller…

    Java 2023年6月15日
    00
  • 一文带你了解SpringBoot中常用注解的原理和使用

    首先我们来介绍一下 Spring Boot 中常用的注解: @SpringBootApplication 这个注解是 Spring Boot 应用程序的入口点,同时也包含了 @EnableAutoConfiguration 和 @ComponentScan 注解,表示使用自动配置和组件扫描。 @RestController 这个注解表示一个类是 RESTfu…

    Java 2023年5月15日
    00
  • Java实现简单的五子棋游戏示例代码

    一、介绍 五子棋是一种非常古老的中国传统游戏,它简单易懂,规则简单,同时又非常有趣,是大众化的棋类游戏之一。本文将介绍如何用 Java 语言实现一个简单的五子棋游戏,让小伙伴们体验一下自己编写游戏的快感。 二、准备工作 开发五子棋游戏需要熟悉 Java 语言的基础代码编写,同时需要掌握一些基础的图形界面编程知识,推荐使用 Swing 或 JavaFX 进行图…

    Java 2023年5月19日
    00
  • Spring Boot 快速集成 Redis的方法

    下面是详细讲解“Spring Boot 快速集成 Redis的方法”的完整攻略。 简介 Redis是目前比较流行的高性能缓存和NoSQL数据库,通过使用它可以提高项目的性能和并发能力。而Spring Boot是目前比较流行的Web框架之一,提供了快速开发、易扩展、自动化配置等特性。下面我们将使用Spring Boot来快速集成Redis。 步骤 第一步:添加…

    Java 2023年5月19日
    00
  • 如何使用MyBatis框架实现增删改查(CRUD)操作

    使用MyBatis框架实现增删改查(CRUD)操作是非常简单的,在下面的攻略中,我将展示如何使用MyBatis框架来完成这些操作。 准备工作 在开始使用MyBatis框架之前,你需要完成以下准备工作: 确定数据库连接信息,包括数据库名称、用户名和密码等。 安装MyBatis框架和相关依赖。 创建数据库表 首先我们需要创建一张用于存储数据的表,例如创建一个名为…

    Java 2023年5月20日
    00
  • IDEA 2019.2.2配置Maven3.6.2打开Maven项目出现 Unable to import Maven project的问题

    下面是针对 “IDEA 2019.2.2配置Maven3.6.2打开Maven项目出现 Unable to import Maven project的问题”的攻略: 问题背景 在使用 IntelliJ IDEA 2019.2.2 配置 Maven 3.6.2 并打开 Maven 项目时,存在无法导入 Maven 项目的问题。 解决方案 第一步:确认 Mave…

    Java 2023年5月20日
    00
  • Java实现BASE64编码和解码的方法

    下面是“Java实现BASE64编码和解码的方法”的完整攻略。 BASE64编码和解码概述 BASE64是一种基于64个可打印字符来表示二进制数据的算法,在网络传输中常用于数据加密和解密、数字签名等领域。 BASE64编码 BASE64编码可以将任意二进制数据编码成可打印的ASCII字符集的代表字符串,常用于将二进制数据在网络传输或者在文本协议中作为参数进行…

    Java 2023年5月20日
    00
  • Java程序执行时间的2种简单方法

    Java程序执行时间的2种简单方法 在Java中,有时需要了解程序的执行时间,以便进行性能优化和调试。本文将详细介绍Java程序执行时间的2种简单方法。 方法1:使用System.currentTimeMillis() 使用System.currentTimeMillis()方法可以简单地获取当前时间的毫秒数,并在程序的不同时间点进行比较,从而计算出程序执行…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部