⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Fight/mianshizhongbeichaoxiaoTokenfangzaiRedislizheibajiwoganchenmole/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

面试被问登录认证怎么做,你说”Token 放 Redis 里”。面试官笑了一下,说你不懂 JWT 的无状态设计。

这场景不少见。知乎上有人专门问过,底下争了半天,说的其实是同一件事:选型没问题,但你没说清楚为什么选。

这篇文章把这件事说清楚:从 JWT 是什么,到放 Redis 的原因,再到面试里怎么回答。

JWT 到底是什么

JWT 全称 JSON Web Token,说白了就是一段字符串,用点号分成三段:Header、Payload、Signature。

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT的三段结构:Header(算法类型)、Payload(用户信息)、Signature(签名),三部分Base64编码后用点号连接

  • Header:声明算法类型(比如 HS256)
  • Payload:存用户信息(用户 ID、角色、过期时间)。只是 Base64 编码,不是加密——任何人拿到 token 都能解码看到内容,密码绝对不能放
  • Signature:用 Header + Payload + 服务端密钥算出来的签名,作用是防篡改

整个验证流程:

JWT认证完整流程:用户登录→服务端签发Token→客户端存储→请求时带上Token→服务端验签

登录成功 → 服务端签发 Token → 客户端保存(一般放 localStorage)→ 每次请求在 Header 里带上 Authorization: Bearer <token> → 服务端验签,从 Payload 取用户信息。全程不查数据库、不查 Redis,自给自足。 这就是”无状态”的意思。

JWT”无状态”的代价:四个致命场景

无状态听起来很美,上线后就知道了。

场景一:账号被盗,无法立即踢人。Token 有效期 2 小时,现在还剩 1 小时 45 分钟。怎么让它立刻失效?纯 JWT 做不到——已签发的 token 在过期前一直有效。

场景二:封禁用户不生效。账号封了,但手里的 token 还能用。

场景三:改密码不踢其他设备。用户改了密码,想让其他设备全部失效。纯 JWT 做不到。

场景四:token 过期吞表单。用户填了 20 多分钟的表单,点提交,token 过期了,整个内容全丢。

这四个场景加在一起,几乎是所有正经业务系统都会碰到的问题。

为什么要把 Token 放 Redis

解决上面这些问题,最直接的方案是黑名单。

用户被踢下线时,把 token 加进黑名单。每次请求先查黑名单,在里面就拒。黑名单存哪?内存不行(多台服务器不共享,重启丢),数据库太慢(每个请求都查一次受不了),Redis 最合适——查询不到 1ms,分布式共享。

// 退出登录时,把 token 加入黑名单
public void logout(String token) {
long expiration = jwtUtil.getExpiration(token);
long ttl = expiration - System.currentTimeMillis();
if (ttl > 0) {
redisTemplate.opsForValue().set(
"blacklist:" + token, "1", ttl, TimeUnit.MILLISECONDS
);
}
}

// 验证 token 时,先查黑名单
public boolean isTokenValid(String token) {
if (redisTemplate.hasKey("blacklist:" + token)) {
return false;
}
return jwtUtil.verify(token);
}

Redis 只存”已失效”的 token,平时没什么写入,查询也快。

还有更彻底的方案:直接把 token 存 Redis,每次验证都去 Redis 查。 这和传统 Session 没太大区别,只是借了 JWT 格式当 Session ID 用。

黑名单 vs 白名单:两种方案的本质区别

方案 存什么 查几次 Redis 能否主动吊销 是否无状态
纯 JWT 不存 0 次 不能 是
JWT+黑名单 存失效的 token 1 次 能 不完全是
token 存 Redis(白名单) 存所有 token 1 次 能 否

黑名单相对轻量,正常请求不写 Redis,只有踢人时才写,多数业务选这个。白名单控制力更强,能做限制同时在线设备数、实时查看在线状态,代价是每次请求都依赖 Redis。

双 Token 续期方案

Token 到期了,用户正在操作怎么办?

  • AccessToken:有效期短(30 分钟),做实际鉴权
  • RefreshToken:有效期长(7 天),只用来换 AccessToken

AccessToken 过期,前端用 RefreshToken 换新的,对用户无感。RefreshToken 存 Redis,用户改密码就删掉,自动让他下次操作时重新登录。

Redis 本身可靠吗

有人担心引入单点故障。Redis 的高可用方案跑了十几年:

  • Sentinel(哨兵模式):主节点挂了自动选新主
  • Cluster(集群模式):数据分片,横向扩展

Redis Cluster架构:数据分片分布在多个节点,支持横向扩展和自动故障转移

性能方面 Redis 单次查询 1ms 以内,单机 QPS 十几万。说”不能放 Redis 因为会单点故障”——这逻辑说不通。

面试官到底在嘲笑什么

两种情况。

第一种:你用了 JWT 但验证时完全不走签名,每次都去 Redis 查 token 是否存在。这等于只借了 JWT 格式,没用它的能力——不如直接生成随机字符串当 Session ID,还省了编解码开销。面试官嘲笑的是这里。

第二种:你确实有业务需求需要主动吊销,选了 JWT+Redis 方案。但面试里只说了”放 Redis”,没说为什么。面试官误以为你不懂无状态设计。

第一种要改方案,第二种要改表达。

纯 JWT 什么时候真的合适

跨服务调用:网关层做一次验签,用户信息往下游传。订单服务、支付服务直接从 JWT 取 userId,不用再查 Redis。这里 JWT 承担的是身份传递角色,不是管理登录会话。

一次性凭证:邮箱验证链接、密码重置链接、临时分享链接。签一次用一次,不需要续期和吊销。

安全要求不高的内部工具:用户量少,不需要踢人下线。纯 JWT 省一层依赖。

面试怎么答

别上来就说”放 Redis”,也别上来就说”JWT 无状态”。

先说业务需求:需不需要主动踢人?改密码后需不需要其他设备立刻失效?需不需要限制同时在线设备数?

然后根据需求说方案:需要的话选 JWT+Redis 黑名单,说清楚纯 JWT 做不了主动吊销;不需要的话纯 JWT 够了,说清楚签名验证流程和密钥管理。

面试官真正想听的不是你选了哪个,是你知不知道每个方案的边界。 “Token 放 Redis”这件事本身没问题,问题是说不出来为什么放。

文章目录
  1. 1. JWT 到底是什么
  2. 2. JWT”无状态”的代价:四个致命场景
  3. 3. 为什么要把 Token 放 Redis
  4. 4. 黑名单 vs 白名单:两种方案的本质区别
  5. 5. 双 Token 续期方案
  6. 6. Redis 本身可靠吗
  7. 7. 面试官到底在嘲笑什么
  8. 8. 纯 JWT 什么时候真的合适
  9. 9. 面试怎么答