首页 / 知识库 / python服务端进阶 / 解析用户登录场景案例

07 - 退出登录:看似简单,其实是最难的一环

“退出登录”听起来是登录里最简单的动作——不就是把登录状态清掉吗?

但如果你用的是 JWT,它其实是整个体系里最反直觉、最容易出错的一环。这一章专门把它讲透。

先看简单的:Session 方案退出,为什么毫不费力

第 03 章说过,Session 方案里,登录状态记在**服务端的账本(Redis)**里。所以退出就一句话:

退出登录 = 把账本上你那一页撕掉

redis.delete(f"session:{session_id}")

删掉之后:

用户下次带着那个 session_id 再来

服务端拿 session_id 去查账本 → 查不到了

"登录已过期,请重新登录" ✅

干净利落。因为状态在服务端,服务端对它有绝对的控制权,想让它失效就让它失效。 这就是第 05 章说的”Session 让服务端管得住”。

再看难的:JWT 方案退出,难在哪

问题的根子,还是第 04 章那个伏笔:JWT 是无状态的,服务端不记账本。

打个最形象的比方:

Session 的凭证像"你在我这存的一笔钱"——我账上把它划掉,你就没了。

JWT 的凭证像"我签给你的一张 2 小时内有效的支票"——
支票已经在你口袋里了,只要没过期、签名是真的,
任何人拿着它来兑现,我都得认。
我总不能追到你口袋里去把支票抢回来吧?

所以当用户点”退出登录”时,服务端面对的尴尬是:

用户手机里那个 token:
   ✅ 签名是真的(服务端自己签的)
   ✅ 还没到过期时间


   按 JWT 的规则,它就是有效的!
   服务端凭什么拒绝它?——默认情况下,拒绝不了。

这就是核心难题:退出登录,本质是要”提前作废一张还没到期的 token”,但 JWT 的设计里压根没给你这个开关。

三种应对思路(各有代价)

既然 JWT 天生没这个能力,就得额外想办法。业界主要有三招:

思路一:黑名单(最常用)

退出时,把这个 token 记到一个”作废名单”里(放 Redis)。之后每次验证时,除了验签名、验过期,再多查一步黑名单

退出:redis 里记下 "这个 token 作废了",过期时间 = token 剩余寿命

验证:验签 ✅ → 验过期 ✅ → 查黑名单:在里面吗?
                                    ├─ 在 → 拒绝(已退出)
                                    └─ 不在 → 放行
  • 代价:每次请求都要查一次 Redis。这等于把”无状态”给破坏了——你又回到了”服务端要存东西、要查东西”的老路。JWT 引以为傲的”不查库”优势没了。
  • 这就是第 06 章演示的方案。它是最直接、最好理解的解法。

思路二:短命 Access Token + 刷新令牌(Refresh Token)

换个思路:既然没法作废,那就让 token 短命到”作废没意义”

给用户发两张证:
   Access Token(干活用):寿命很短,比如 15 分钟
   Refresh Token(续命用):寿命长,比如 7 天,专门用来换新的 Access Token

平时:拿 Access Token 访问接口
过期:Access 过期了,用 Refresh Token 悄悄换一张新的(用户无感)
退出:把服务端记录的 Refresh Token 作废,不让你再续命
      → 手里那张 Access Token 最多再撑 15 分钟就自然失效
  • 代价:架构变复杂(要管两种 token、要有刷新接口)。而且退出后,那张短命的 Access Token 在剩下的十几分钟里理论上仍然有效——只是因为它太短命,风险被压到很小,一般能接受。
  • 这是目前最主流的工程做法,第 09 章会展开讲双令牌。

思路三:改签名密钥(“核武器”,一般不用)

服务端换掉那个签发用的密钥。密钥一换,所有旧 token 的签名瞬间全部对不上,全体失效。

  • 代价:这是”一键全员下线”,会把所有用户都踢下线,不是针对某一个人。所以它只用于极端情况(比如密钥泄露了),日常退出登录绝不会用它。

三种思路对比

思路能不能精确作废单个用户代价适用
黑名单✅ 能每次请求多查一次 Redis,牺牲无状态需要”立刻、精确”作废
短命 + 刷新令牌半能(最多延迟十几分钟)架构复杂,退出后短暂残留主流选择,安全性和体验平衡
换密钥❌ 只能全员一起全体用户被踢下线密钥泄露等紧急事故

实际项目里,思路二和思路一常常一起用:平时靠短命 token 兜底,遇到”退出登录""改密码""管理员强制下线”这种需要立刻生效的场景,就把对应的 Refresh Token(或该用户的 token)拉进黑名单。

一个必须想清楚的连带问题:改密码 / 账号被盗

退出登录的难题,其实是一类问题的代表。下面这些场景,本质都是”要提前作废已发出的 token”,全都撞上同一堵墙:

用户改了密码       → 旧 token 该不该失效?(应该,否则改密码没意义)
发现账号被盗       → 要立刻把盗号者踢下线
管理员封禁某用户   → 要立刻让他的所有 token 失效
用户在新设备登录   → 要不要挤掉旧设备?

如果你用纯 JWT,这些需求你都做不到——因为你没有作废 token 的能力。所以只要业务里有上面任何一条需求,你就必然要引入服务端存储(黑名单/版本号),也就必然要放弃一部分”无状态”。

一个常用技巧:在用户记录里存一个 token_version 版本号,签发 token 时把版本号写进去。改密码/踢下线时,把用户的版本号 +1。验证时比对版本号,对不上就拒绝。这样一次就能作废该用户的所有 token,而不用逐个拉黑。

小结

  • 退出登录的难度完全取决于你选了哪条路线:Session 极简单,JWT 很棘手。
  • 根因:JWT 无状态,服务端没有”作废一张已发出 token”的开关(像追不回已经签出去的支票)。
  • 三种应对:黑名单(精确但牺牲无状态)、短命 token + 刷新令牌(主流,有短暂残留)、换密钥(核武器,全员下线)。
  • “改密码、踢下线、封号、账号被盗”和退出登录是同一类问题——只要有这些需求,纯 JWT 就不够用,必须引入服务端状态。
  • 记住这句话:你想让 JWT 能被”退出/作废”,就得给它加上服务端存储,也就等于放弃了它一部分无状态的优势。天下没有免费的午餐。

到这里,登录的核心机制就全通了。接下来两章讲”周边”:先是绕不开的安全问题(密码怎么存、凭证怎么被偷),再是进阶场景(双令牌、多端登录、SSO)。


上一章 ← 06 - 完整例子:一次登录到退出的旅程 | 下一章 → 08 - 绕不开的安全问题 | 回到 README 目录