文章目录
- 同端登录冲突检测
- 需求分析
- 方案设计
- 1、账号冲突检测策略
- 2、实现思路
- Sa-Token 入门
- 入门 Demo
- Sa-Token 实用功能
- Sa-Token 原理
同端登录冲突检测
需求分析
在多用户系统中,如电商平台、企业办公系统或社交应用,用户的账户安全和系统的正常使用至关重要。
如果同一个账户同时在多个设备上登录,可能由于之前在别的地方临时登录后忘记退出账号,导致数据泄露或账户被滥用等问题。此外,还有可能会导致数据不一致问题,例如重复提交订单、聊天记录不同步等。
针对我们会员制的刷题网站,若会员账户被不法分子售卖或多人共享,将会损害公司利益。
为了防止这些情况,我们的系统需要能够实时监控和检测同一账户在多个设备上的登录情况,在检测到冲突时,及时通知用户并采取相应的安全措施(如强制下线或警告)。
方案设计
1、账号冲突检测策略
常见业务上有 3 种冲突检测策略:
1)单点登录模式
同一时间,只允许同一账户在一个设备上登录。即每次新设备登录时,检测当前账户是否已经在其他设备上登录,若已登录则将其他设备强制下线。
一般应用于企业内部系统或包含敏感数据的系统中。
2)多设备登录限制
允许同一账户在多个设备上登录,但需限制设备数量(如最多两台)。即每次登录时,检测当前账户已登录设备数量,若超过限制,系统阻止登录或强制最早登录设备下线。
这种模式常见于视频网站、买断制 / 订阅制软件。
一个软件激活码只能同时在 X 台设备使用,也是类似的机制。
3)同设备类型限制
允许同一账户在不同设备类型(如手机和 PC)上同时登录,但同一类型设备只能登录一个。例如,用户可同时在电脑和 iPad 上使用我们的平台,但不能在两台手机上登录在线。(QQ 就是这样)
要想实现这种策略,需要记录账户登录设备的类型,每次登录时检查是否有同类设备已登录,如果有则强制下线相同设备类型的旧登录。
这种策略常应用于一些需兼顾多端体验的应用。比如我们的面试刷题平台用户,经常有 iPad 和电脑端同时刷题的需求,因此我们采用第 3 种策略 同设备类型限制(同端互斥登录)。
2、实现思路
如何实现同端互斥登录呢?
- 用户登录时获取当前设备信息(通过 User-Agent 获取)
- 将用户登录信息与设备信息一起保存(本地或三方缓存中)
- 用户登录时判断同设备是否已经登录(本地或缓存中是否已存在)
- 如果检测到冲突,可以直接顶号(将前一个设备下线,也就是移除登录态)
其实自主实现这个需求也并不难,但涉及到登录这样的核心业务场景,经验不足的情况下,更建议使用一些成熟的第三方框架。比如轻量级 Java 权限认证框架 内置了 同端互斥登录功能,可以更快更稳地实现。
以下为 Sa-Token 同端互斥示意图:
借这个场景,带大家学习下 Sa-Token 框架。
Sa-Token 入门
Sa-Token 是一个轻量级 Java 权限认证框架,主要解决:登录认证、权限认证、单点登录、OAuth2.0、分布式Session会话、微服务网关鉴权等一系列权限相关问题。
Sa-Token 功能一览:
Sa-Token 旨在以简单、优雅的方式完成系统的权限认证部分,以登录认证为例,只需要一行代码:
// 会话登录,参数填登录人的账号id
StpUtil.login(10001);
无需实现任何接口、无需创建任何配置文件,只需要这一句静态代码的调用,便可以完成会话登录认证!
如果一个接口需要登录后才能访问,我们只需调用以下代码来检测该用户是否已经登录:
// 校验当前客户端是否已经登录,如果未登录则抛出 `NotLoginException` 异常
StpUtil.checkLogin();
由于是国人开发,它的官方文档](https://sa-token.cc/doc.html#/) 写得非常通俗易懂,是纯天然的教程,基本上按照文档的顺序阅读一遍,就能够上手了。下面我们就先试着 参考文档 来运行下官方的 Demo。
入门 Demo
在 Spring Boot 项目中,可以通过sa-token-spring-boot-starter 快速引入 Sa-Token。注意版本:
<!-- Sa-Token 权限认证 -->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.39.0</version>
</dependency>
Sa-Token 支持通过配置定制框架的行为,比如登录态过期时间、不活跃自动下线等:
server:# 端口port: 8081############## Sa-Token 配置 (文档: https://sa-token.cc) ##############
sa-token: # token 名称(同时也是 cookie 名称)token-name: satoken# token 有效期(单位:秒) 默认30天,-1 代表永久有效timeout: 2592000# token 最低活跃频率(单位:秒),如果 token 超过此时间没有访问系统就会被冻结,默认-1 代表不限制,永不冻结active-timeout: -1# 是否允许同一账号多地同时登录 (为 true 时允许一起登录, 为 false 时新登录挤掉旧登录)is-concurrent: true# 在多人登录同一账号时,是否共用一个 token (为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 token)is-share: true# token 风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)token-style: uuid# 是否输出操作日志 is-log: true
对于入门 Demo,我们可以不写配置,直接零配置启动项目。
然后我们编写 2 个测试接口,一个用于登录,一个用于检测是否已登录:
/*** 测试 Sa-Token 框架实现用户登录*/
@RestController
@RequestMapping("/test/user/")
public class TestSaTokenLoginController {// 测试登录,浏览器访问: http://localhost:8101/api/test/user/doLogin?username=zhang&password=123456@RequestMapping("doLogin")public String doLogin(String username, String password) {// 此处仅作模拟示例,真实项目需要从数据库中查询数据进行比对if ("zhang".equals(username) && "123456".equals(password)) {StpUtil.login(10001);return "登录成功";}return "登录失败";}// 查询登录状态,浏览器访问: http://localhost:8101/api/test/user/isLogin@RequestMapping("isLogin")public String isLogin() {return "当前会话是否登录:" + StpUtil.isLogin();}
}
然后可以通过直接访问接口地址来验证。
Sa-Token 实用功能
按顺序阅读 官方文档 学习即可,重点学习下图中红圈里的内容,其他的作为可选学习项:
Sa-Token 原理
推荐阅读 Sa-Token 文档的 附录部分,尤其是对前后端分离的登录模式不太熟悉的同学。重点阅读以下红圈部分:
Sa-Token 认证流程图,要重点掌握。如果让你自己设计一个权限校验框架,就是按这个流程来:
要理解 Sa-Token 的 Session 模型,区别于 Servlet 的 HttpSession,Sa-Token 维护了自己的 Session,共有 3 种 Session 创建时机:
- Account-Session: 指的是框架为每个 账号 id 分配的 Session。是分配给账号id的,而不是分配给指定客户端的,也就是说在 PC、APP 上登录的同一账号所得到的 Session 也是同一个,所以两端可以非常轻松的同步数据。
Token-Session: 指的是框架为每个 token 分配的 Session·
。不同的设备端,哪怕登录了同一账号,只要它们得到的 token 不一致,它们对应的Token-Session就不一致,这就为我们不同端的独立数据读写提供了支持。比如实现“指定客户端超过两小时无操作就自动下线,如果两小时内有操作,就再续期两小时,直到新的两小时无操作”。Custom-Session
: 指的是以一个特定的值作为 SessionId,来分配的 Session。不依赖特定的账号id 或者 token,当成一个 Map 去使用即可,比如可以为一个团队的用户指定相同的 SessionId,让一个团队最多 N 个用户同时在线等。