面试题:如何限制一个账号只能在一处登录?

160 阅读4分钟

Hello,大家好,我是 Sunday。

昨天,有位同学在面试的过程中被问到了一个很有意思的问题:【如何限制一个账号只能在一处登录?】,既:单设备登录

PS:很多同学会把 单设备登录单点登录 搞混,但是他们之间本质上是不一样的。

  • 单设备登录:同一账号 在同一时间只能在一个设备上登录,如果用户在另一台设备上登录,则之前的设备会被自动下线。
  • 单点登录(SSO):用户在多个系统或应用之间只需要登录一次,就可以访问所有相关系统,无需再次输入凭据。

言归正传,咱们回到当前面试题本身 如何限制一个账号只能在一处登录?

技术方案

要实现 单设备登录,我们需要设立一种机制,确保同一账号在不同设备上不能同时保持活跃。

常见的实现方式仅从前端角度来说的话,主要有两种:

1:基于 Token 版本控制(推荐)

核心思想:Token 版本号(token_version)充当唯一凭证,每次新设备登录时,版本号 +1,旧 Token 失效。

我们来拆解下这个流程:

首先第一步: 用户首次登录

  • 用户登录后,数据库中的 token_version 设为 1
  • 后端生成 JWT Token,并在 payload 中加入 { userId: 1, tokenVersion: 1 }
  • 返回 Token 给前端,前端存储在 localStorageAuthorization 头中。

然后第二步: 用户在新设备登录

  • 新设备登录后,后端查询该用户的 token_version,然后 +1,更新到数据库(如 token_version=2)。
  • 生成新的 Token { userId: 1, tokenVersion: 2 },返回给新设备。

第三步: 旧设备携带 Token 访问接口

  • 旧设备的 Token 仍然是 { userId: 1, tokenVersion: 1 },但数据库中 token_version=2
  • 服务器验证 Token,发现 tokenVersion !== 数据库的 token_version,返回 401,拒绝访问。

第四步: 旧设备强制下线

  • 旧设备前端收到 401 响应后:
    • 清除本地 Token
    • 跳转到登录页
    • 提示 "账号已在其他设备登录,请重新登录"

以上方案是实现 单设备登录 最简单的方案,核心就是维护了 token_version 的版本值

2:基于 WebSocket 的实时强制下线

核心思想每次用户在新设备登录时,服务器通过 WebSocket 通知旧设备下线,旧设备收到消息后清除 Token 并强制登出。

咱们通常拆解下这个流程:

首先第一步: 用户首次登录

  • 用户登录后,服务器建立 WebSocket 连接,并在 Redis 或者 数据库中存储对应状态,如: { userId: 1, socketId: "xyz123" } 以记录该设备的 WebSocket 连接 ID。

然后第二步:用户在新设备登录

  • 新设备登录后,服务器检测到该用户已有活跃连接。
  • 给旧设备发送 WebSocket 消息"你的账号已在其他设备登录,请重新登录"
  • 删除旧设备的 WebSocket 连接记录
  • 新设备建立 WebSocket 连接,存储 { userId: 1, socketId: "abc456" }

第三步:旧设备收到 WebSocket 消息

  • 旧设备监听到 "被踢下线" 消息:
    • 清除本地 Token
    • 自动跳转到登录页
    • 提示 "账号已在其他设备登录"

与上一个相比,这里核心就是利用了 WebSocket 双向通讯 的能力来实现 强制下线 的功能,好处是可以做到 即时响应。麻烦的地方是需要重新对接 ws 协议。


我们可以通过以下表格来对比下两种方案的差异:

方案优势劣势
基于 Token 版本控制逻辑简单,后端维护 Token 版本即可,兼容性好需要前端主动处理 401,可能有短暂的延迟
基于 WebSocket 实时强制下线即时性强,用户体验更好需要 WebSocket 连接,维护连接状态,断连时需要额外处理

当然,这两种方案也可以结合起来进行使用:

WebSocket 优先,当 WebSocket 连接异常时,后端回退到 Token 版本校验。这样既能 保证即时强制下线,也能 兼容不支持 WebSocket 的场景

前端训练营:1v1 私教,9 大服务,终身辅导,帮你拿到满意的 offer 已帮助数百位同学拿到了中大厂 offer