4. Redis 与 JWT 集成架构设计

在 SaaS 鉴权链路中,JWT 作为无状态令牌承载用户身份与权限声明,Redis 则作为高性能缓存层承载令牌的校验、黑名单、刷新与分布式会话状态。本章从整体架构图出发,明确 Token 缓存层的定位,并系统阐述缓存穿透、击穿、雪崩三大风险的防护思路。

4.1 整体架构图:SaaS 鉴权拦截链路缓存层

下图展示了从客户端请求到后端服务的完整鉴权链路,Redis 作为核心缓存层位于网关与业务服务之间,承担 Token 校验、黑名单、刷新令牌与限流计数等职责。

SaaS 鉴权拦截链路缓存层架构图 SaaS 鉴权拦截链路缓存层架构图 客户端 API 网关 Redis 缓存层 Token 校验 / 黑名单 / 刷新 业务服务 数据库 链路说明 1. 客户端请求携带 JWT 到达 API 网关。 2. 网关从 Redis 缓存层查询 Token 有效性、黑名单状态与权限缓存。 3. 若缓存命中且有效,网关将请求转发至业务服务;若未命中或失效,触发回源校验。 4. 业务服务在需要时从 Redis 获取用户会话数据或刷新令牌。 5. 数据库仅作为持久化兜底,Redis 承担 95% 以上的鉴权请求。 6. 缓存穿透、击穿、雪崩防护策略在 Redis 层统一实施。

4.2 Token 缓存层定位

Token 缓存层并非简单的键值存储,而是 SaaS 鉴权体系中的“状态决策中心”。其核心定位体现在以下三个维度:

维度 定位说明 典型 Key 设计
校验加速 缓存 JWT 签名校验结果与用户权限快照,避免每次请求都解密 JWT 或查询数据库 auth:token:{jti}:valid
状态管理 维护 Token 黑名单、刷新令牌、设备登录状态等动态数据 auth:blacklist:{jti}
限流与熔断 基于 Redis 计数器和滑动窗口实现接口级限流,保护下游服务 auth:ratelimit:{userId}:{api}
设计原则:Token 缓存层应遵循“读多写少、最终一致、自动过期”原则。JWT 本身无状态,Redis 缓存层为其补充了必要的运行时状态,但不可将 Redis 作为持久化存储,所有缓存数据均应设置 TTL。

4.3 缓存穿透防护思路

缓存穿透指请求查询一个根本不存在的数据(如伪造的 JWT),导致每次请求都穿透 Redis 直达数据库,造成数据库压力激增。

4.3.1 防护策略对比

策略 实现方式 适用场景 注意事项
空值缓存 查询结果为空时,在 Redis 中缓存一个短 TTL 的空标记 Token 黑名单、用户不存在等场景 TTL 不宜过长(建议 30-60 秒),防止数据已存在但缓存仍为空
布隆过滤器 在网关层前置布隆过滤器,快速判断 Token 是否可能存在 大规模 SaaS 租户场景,Token 总量百万级以上 存在误判率,需结合空值缓存兜底
参数校验 在进入缓存层前对 JWT 格式、签名、过期时间做基础校验 所有请求必经之路 不能完全替代缓存层防护,但可过滤大量恶意请求

4.3.2 空值缓存代码示例

// 查询 Token 校验结果
public Boolean checkTokenValid(String jti) {
    String key = "auth:token:" + jti + ":valid";
    String cached = redisTemplate.opsForValue().get(key);
    if (cached != null) {
        return "true".equals(cached);
    }
    // 回源校验 JWT 签名与黑名单
    Boolean valid = doRealValidation(jti);
    // 无论是否有效都写入缓存,无效时设置短 TTL
    if (valid) {
        redisTemplate.opsForValue().set(key, "true", 300, TimeUnit.SECONDS);
    } else {
        redisTemplate.opsForValue().set(key, "false", 30, TimeUnit.SECONDS);
    }
    return valid;
}

4.4 缓存击穿防护思路

缓存击穿指一个热点 Token(如超级管理员 Token)在缓存过期瞬间,大量并发请求同时回源查询数据库,导致数据库瞬时压力飙升。

4.4.1 防护策略

  • 互斥锁(Mutex Key):当缓存失效时,只允许一个线程回源加载数据,其他线程等待或快速失败。
  • 逻辑过期:缓存数据永不过期,但数据中携带逻辑过期时间,异步线程负责刷新。
  • 热点预加载:针对已知热点 Token(如 SaaS 平台内置管理员),在缓存即将过期前主动刷新。

4.4.2 互斥锁实现示例

public String getTokenWithMutex(String jti) {
    String key = "auth:token:" + jti + ":valid";
    String value = redisTemplate.opsForValue().get(key);
    if (value != null) {
        return value;
    }
    // 尝试获取分布式锁
    String lockKey = "lock:token:" + jti;
    Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS);
    if (Boolean.TRUE.equals(locked)) {
        try {
            // 双重检查,防止在获取锁期间缓存已被其他线程重建
            value = redisTemplate.opsForValue().get(key);
            if (value != null) {
                return value;
            }
            // 回源加载
            value = loadFromDatabase(jti);
            redisTemplate.opsForValue().set(key, value, 300, TimeUnit.SECONDS);
            return value;
        } finally {
            redisTemplate.delete(lockKey);
        }
    } else {
        // 未获取到锁,等待后重试或返回降级数据
        Thread.sleep(50);
        return getTokenWithMutex(jti); // 递归重试
    }
}

4.5 缓存雪崩防护思路

缓存雪崩指大量缓存数据在同一时间过期,或 Redis 节点宕机,导致所有请求直接压向数据库,引发连锁故障。

4.5.1 防护策略

策略 实现方式 效果
过期时间随机化 在基础 TTL 上增加随机偏移(±30%) 避免大量 Key 同时过期
多级缓存 本地缓存(Caffeine)+ Redis 缓存组合 Redis 宕机时本地缓存仍可提供服务
Redis 高可用 主从 + Sentinel 或 Redis Cluster 单点故障自动切换
限流降级 网关层对回源请求实施限流,超出阈值直接返回降级响应 保护数据库不被冲垮

4.5.2 过期时间随机化代码示例

// 生成随机 TTL,基础 300 秒,随机偏移 ±30%
public long getRandomTtl(long baseTtl) {
    double factor = 0.7 + Math.random() * 0.6; // 0.7 ~ 1.3
    return (long) (baseTtl * factor);
}

// 设置缓存时使用随机 TTL
public void setTokenCache(String jti, String value) {
    String key = "auth:token:" + jti + ":valid";
    long ttl = getRandomTtl(300);
    redisTemplate.opsForValue().set(key, value, ttl, TimeUnit.SECONDS);
}

4.6 三大风险防护思路总结

缓存穿透/击穿/雪崩防护思路总结 缓存穿透 / 击穿 / 雪崩防护思路总结 缓存穿透 核心问题:查询不存在的数据 • 空值缓存 + 短 TTL • 布隆过滤器前置过滤 • 请求参数基础校验 缓存击穿 核心问题:热点 Key 过期瞬间 • 互斥锁(Mutex Key) • 逻辑过期 + 异步刷新 • 热点预加载 缓存雪崩 核心问题:大量 Key 同时过期 / 宕机 • TTL 随机化 • 多级缓存(本地 + Redis) • Redis 高可用 + 限流降级 综合防护建议 穿透 + 击穿 + 雪崩防护需组合使用,不可依赖单一策略。建议在网关层统一实施,对业务服务透明。
重要提醒:缓存穿透、击穿、雪崩三种风险可能同时发生。例如,Redis 节点宕机(雪崩)期间,大量恶意请求携带伪造 JWT(穿透)同时回源,若未实施限流降级,数据库将瞬间崩溃。因此,防护策略必须分层组合,形成纵深防御体系。