3. 鉴权拦截核心概念:Token、Session、JWT、OAuth2.0 基础概念与 Redis 缓存的关系
在构建后端鉴权拦截系统时,Token、Session、JWT 和 OAuth2.0 是四个最核心的概念。它们各自解决了不同场景下的身份认证与授权问题,而 Redis 作为高性能缓存数据库,在其中扮演了关键的“状态存储”与“令牌管理”角色。理解它们之间的区别与联系,是设计可靠鉴权架构的前提。
3.1 核心概念速览
| 概念 | 本质 | 状态存储位置 | 是否自包含 | Redis 典型作用 |
|---|---|---|---|---|
| Session | 服务端会话对象 | 服务端内存 / Redis | 否(依赖服务端存储) | 集中存储 Session 数据,实现分布式会话共享 |
| Token | 访问凭证字符串 | 客户端持有,服务端可验证 | 视实现方式而定 | 存储黑名单、刷新令牌、速率限制计数器 |
| JWT | 自包含的 Token 标准 | 客户端持有,服务端无状态验证 | 是(包含用户信息与签名) | 存储 JWT 黑名单、刷新令牌、临时授权码 |
| OAuth2.0 | 授权框架协议 | 授权服务器 + 资源服务器 | 否(依赖授权码、令牌端点) | 存储授权码、访问令牌、刷新令牌、客户端信息 |
工程经验: 在微服务架构中,Session 模式因需要共享状态而逐渐被 JWT 替代,但 JWT 的“无状态”特性也带来了无法主动失效的问题。Redis 恰好弥补了这一缺陷——通过维护 JWT 黑名单,实现“有状态的无状态鉴权”。
3.2 Session 与 Redis:从单体到分布式
传统的 Session 鉴权流程:用户登录后,服务端创建 Session 对象,将 Session ID 写入 Cookie 返回给客户端。后续请求携带 Cookie,服务端根据 Session ID 查找内存中的 Session 数据。
单体架构下的问题: 当应用部署多台服务器时,用户的请求可能被负载均衡转发到不同节点,导致 Session 数据无法共享(Session 丢失)。
Redis 解决方案: 将 Session 数据从本地内存迁移到 Redis 集中存储。所有应用节点通过统一的 Redis 读取 Session,实现跨节点会话共享。
// Spring Session + Redis 配置示例
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)
public class RedisSessionConfig {
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory("redis-master", 6379);
}
}
关键指标: 使用 Redis 存储 Session 后,单次 Session 读取延迟通常 < 1ms(内网),支持 TTL 自动过期,无需手动清理过期会话。
3.3 JWT 与 Redis:无状态令牌的有状态补充
JWT(JSON Web Token)由 Header、Payload、Signature 三部分组成,服务端通过验证签名即可确认令牌合法性,无需查询数据库。但“无状态”也意味着:
- 无法主动失效: 签发后直到过期前都有效,无法强制用户下线。
- 无法检测令牌泄露: 被盗用的 JWT 在有效期内可被任意使用。
- Payload 大小受限: 不宜存放过多用户信息,否则每次请求都携带大量数据。
Redis 的补充角色:
- JWT 黑名单(Blacklist): 用户登出或修改密码时,将当前 JWT 的 jti(JWT ID)存入 Redis,设置 TTL 为令牌剩余有效期。鉴权拦截时先检查 Redis 黑名单。
- 刷新令牌(Refresh Token)存储: 长期有效的 Refresh Token 存入 Redis,访问令牌过期后通过 Refresh Token 换取新令牌。
- 临时授权码(Authorization Code): OAuth2.0 授权码流程中,授权码存入 Redis,设置短 TTL(通常 5 分钟)。
// JWT 黑名单校验伪代码
public boolean isTokenBlacklisted(String jti) {
// 使用 Redis 的 EXISTS 命令检查
return redisTemplate.hasKey("jwt:blacklist:" + jti);
}
// 用户登出时加入黑名单
public void logout(String jti, long ttlSeconds) {
redisTemplate.opsForValue().set(
"jwt:blacklist:" + jti,
"1",
ttlSeconds,
TimeUnit.SECONDS
);
}
避坑提示: JWT 黑名单的 TTL 必须与令牌剩余有效期一致,否则会出现“黑名单已过期但令牌仍有效”的安全漏洞。建议在签发 JWT 时记录过期时间戳,登出时计算剩余秒数。
3.4 OAuth2.0 与 Redis:授权码模式的缓存基石
OAuth2.0 定义了四种授权模式,其中授权码模式(Authorization Code Grant)是最常用、最安全的流程。Redis 在该模式中承担了多个关键数据的临时存储:
| 数据项 | Redis Key 设计 | TTL | 说明 |
|---|---|---|---|
| 授权码 | oauth:code:{code} |
5 分钟 | 一次性使用,使用后立即删除 |
| 访问令牌 | oauth:access:{token} |
2 小时 | 资源服务器通过 Redis 验证令牌有效性 |
| 刷新令牌 | oauth:refresh:{token} |
30 天 | 用于续期访问令牌,支持轮换策略 |
| 客户端信息 | oauth:client:{clientId} |
长期(无过期) | 缓存客户端密钥、回调地址、授权范围 |
工程实践: 在 Spring Security OAuth2 中,默认使用
InMemoryTokenStore 或 JdbcTokenStore。生产环境建议替换为 RedisTokenStore,利用 Redis 的原子性操作和 TTL 机制,避免数据库频繁读写。
3.5 鉴权拦截中的 Redis 数据流
下图展示了在 OAuth2.0 + JWT 混合架构中,Redis 如何贯穿整个鉴权拦截流程:
3.6 四种鉴权模式的 Redis 选型建议
| 场景 | 推荐方案 | Redis 数据结构 | 注意事项 |
|---|---|---|---|
| 传统单体应用 | Session + Redis | String(Session 序列化) | 注意 Session 序列化性能,避免大对象 |
| 微服务内部鉴权 | JWT + Redis 黑名单 | Set / String(黑名单) | 黑名单 TTL 必须精确计算,建议使用 Lua 脚本原子操作 |
| 第三方开放平台 | OAuth2.0 + Redis TokenStore | Hash / String(令牌+客户端信息) | 授权码必须一次性使用,使用 Redis 的 DEL 原子性 |
| 移动端 / SPA | JWT + Refresh Token | String(Refresh Token)+ Set(黑名单) | Refresh Token 支持轮换,旧令牌立即失效 |
3.7 常见故障与排查方向
- Session 丢失: 检查 Redis 连接是否正常、Session Key 是否被误删除、TTL 是否过短。
- JWT 黑名单失效: 确认黑名单 TTL 计算逻辑是否正确,Redis 时钟是否与服务器同步。
- OAuth2.0 授权码重复使用: 检查授权码消费时是否使用 Redis 的原子删除操作(
GET + DEL或 Lua 脚本)。 - Redis 内存暴涨: 检查令牌黑名单是否未设置 TTL,或 Refresh Token 未及时清理。
核心结论: 无论采用 Session、JWT 还是 OAuth2.0,Redis 都是鉴权拦截系统中不可或缺的“状态层”。它解决了分布式环境下的会话共享、令牌主动失效、临时凭证存储等核心问题。理解这四者的关系,是设计高可用鉴权架构的第一步。