Skip to content

jwt 的 token 失效机制

核心概念汇总

  1. JWT 是什么?
    • JWT (JSON Web Token) 是目前最流行的跨域认证解决方案。
    • 特点:自包含、无状态、安全性、可扩展、可跨域、可加密。
    • 缺点:无法主动失效、无法主动更新。
  2. JWT 的 token 失效机制:
    • 使用 JWT 要非常明确的一点:JWT 失效的唯一途径就是等待时间过期。
    • 黑名单机制:将失效的 token 存储在数据库中,每次请求时,检查 token 是否在黑名单中。
    • 白名单机制:将有效的 token 存储在数据库中,每次请求时,检查 token 是否在白名单中。
      • 不管是记录白名单还是黑名单,都需要一个记录的地方。对于分布式系统,还是需要每次使用前去记录的地方查询对比,和 session 方案没本质区别。
  3. 最佳实践:
    • JWT 失效机制:合理设置过期时间,使用 Refresh Token 延长会话。
      • 使用 Refresh Token 延长会话,可以避免频繁刷新 Token,提高用户体验。
      • Refresh Token 的过期时间应该比 JWT 的过期时间更长。
      • 参考以上【黑/白名单机制】。
    • 最小化信息存储:避免存储敏感数据,简化 JWT Payload。
      • 服务器的黑名单(或白名单)只存储 jti,而不是整个 JWT。
    • 安全存储与传输:使用 HttpOnly Cookie 存储,确保通过 HTTPS 传输。
    • 密钥管理与加密算法:使用强加密算法,定期轮换密钥。
    • 防止重放攻击:通过 jti 防止 Token 被重放。
    • 最小权限原则:控制角色和权限,实施细粒度权限管理。
    • 跨服务认证:实现 JWT 跨服务认证,使用 API Gateway 简化验证。

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案,是一种基于 Token 的认证授权机制。从 JWT 的全称可以看出,JWT 本身也是 Token,一种规范化之后的 JSON 结构的 Token

因为 JWT 是无状态的,所以它的有效期完全由其本身决定,也就是说服务端无法让一个 token 失效。

一、token 失效机制

Token 失效机制是保证系统安全性的重要部分。通过合理的失效设计,可以减少攻击风险,防止被盗 Token 长时间滥用。以下是 Token 失效机制的常见实现方式及其具体补充:

1. 时间失效

通过设置有效期限制 Token 的使用时间。

(1) 短期有效的 Access Token

  • 设置较短的有效期(如 5-15 分钟)。
  • 优点:即使 Token 被截获,攻击者使用时间受限。
  • 实现:
    • 在生成 Token 时,添加 exp(过期时间)。
      json
      {
        "sub": "1234567890",
        "name": "John Doe",
        "exp": 1714656000 // Unix 时间戳
      }
    • 后端验证时检查 Token 是否过期。

(2) 长期有效的 Refresh Token

  • 用于延长会话有效期,通常有效期较长(如 7 天或 30 天)。
  • 客户端用 Refresh Token 获取新的 Access Token。
  • 实现:
    • 在 Access Token 过期时,后端验证 Refresh Token 是否有效,签发新的 Access Token。

2. 主动注销机制

在用户登出或敏感操作后,主动让 Token 失效。

实现方式:

  • 后端黑名单机制

    • 在用户登出时,将 Token 的唯一标识(如 jti 或 Token 哈希值)加入黑名单。

    • 在每次请求时,检查 Token 是否在黑名单中。

    • 黑名单存储可以使用 Redis 实现:

      javascript
      const redis = require("redis");
      const client = redis.createClient();
      
      function addToBlacklist(tokenId) {
        client.set(tokenId, "blacklisted", "EX", 3600); // 设置过期时间
      }
      
      function isBlacklisted(tokenId, callback) {
        client.get(tokenId, callback);
      }
  • 删除 Refresh Token

    • 在数据库中存储 Refresh Token,当用户注销时,移除对应记录。

3. IP 和设备绑定失效

限制 Token 的使用环境,通过校验客户端信息使 Token 失效。

实现方式:

  • 在 Token 中嵌入客户端的 IP、User-Agent 等信息。
  • 后端每次校验 Token 时,验证这些信息是否与当前请求一致。
  • 示例:
    json
    {
      "sub": "1234567890",
      "name": "John Doe",
      "ip": "192.168.0.1",
      "userAgent": "Mozilla/5.0"
    }

4. 超时失效(会话过期)

Token 在一段时间未使用后自动失效。

实现方式:

  • 设置滑动过期时间(Sliding Expiration):

    • 每次使用 Token 时,刷新其过期时间。
    • 示例:用户请求刷新时,更新 Token 的过期时间。
      javascript
      function generateToken(payload) {
        const expiration = Math.floor(Date.now() / 1000) + 900; // 15 分钟
        return jwt.sign({ ...payload, exp: expiration }, secretKey);
      }
  • 服务端记录 Token 的最后使用时间:

    • 结合 Redis 或数据库记录最后一次验证时间,判断是否超时。

5. 异常检测失效

当检测到异常行为时,强制让 Token 失效。

触发场景:

  • 检测到多地点或多设备登录同一账号。
  • 多次失败的登录尝试。
  • 短时间内的高频请求。

实现方式:

  • 使用异常事件触发 Token 加入黑名单。
  • 例如:
    • 一个用户从多个地理位置使用同一 Token 时,触发安全机制强制失效。
    • 通过 Redis 或数据库存储设备和位置信息。

6. 强制失效机制

管理员手动或系统定时让所有 Token 强制失效。

实现方式:

  • 全局签发版本号 (Token Versioning)
    • 为每个用户分配一个版本号,在 Token 中携带。
    • 当需要强制失效时,更新版本号,旧 Token 即失效。
    • 示例:
      • 用户数据库记录:
        json
        { "userId": 123, "tokenVersion": 2 }
      • Token Payload:
        json
        { "userId": 123, "tokenVersion": 2 }

总结:

Token 失效机制的核心在于多层防护

  1. 时间约束:通过有效期限制使用范围。
  2. 行为约束:结合用户行为检测异常。
  3. 动态控制:主动注销、全局失效等增强安全性。

根据业务场景合理组合这些机制,既能提高安全性,也能避免用户体验受损。

二、token 失效原因

Token 失效的原因主要涉及以下四个方面:

  1. 时间相关:如超时、滑动过期机制缺失。
  2. 用户行为:如注销、误操作。
  3. 环境变动:如设备冲突、网络环境变化。
  4. 安全策略:如异常检测、权限调整。

三、token 失效解决方案

1. 短期 Token + 刷新机制(用户登录返回两个 JWT)

场景:用户登录后需要持续访问资源,短期 Token 过期可能中断操作。

解决方案

  • 短期 Access Token
    • 设置短生命周期的 Access Token(如 5-15 分钟)。
    • 通过短期有效 Token 减少暴露时间,提高安全性。
  • 长期 Refresh Token
    • 通过 Refresh Token 在后台静默获取新的 Access Token,无需用户重复登录。
  • 实现步骤
    1. 用户登录后,服务器返回 Access Token 和 Refresh Token。
    2. Access Token 过期时,客户端自动使用 Refresh Token 请求新的 Access Token。
    3. 如果 Refresh Token 也失效,要求用户重新登录。

优点

  • 提高安全性,减少长期 Token 暴露风险。
  • 静默刷新不会影响用户体验。

注意事项

  • Refresh Token 存储在安全位置(如 HttpOnly Cookie)。
  • 设计 Refresh Token 的失效机制,避免滥用。

2. 提前刷新机制

场景:用户操作较长时间未主动发出请求,Token 可能在用户下次操作时过期。

解决方案

  • 客户端定时检测 Token 剩余有效时间,提前触发刷新。
  • 刷新条件:
    • Token 剩余有效时间低于某个阈值(如 2 分钟)。
  • 实现步骤
    1. 客户端在获取 Token 后开始倒计时。
    2. 每次发送请求时,检查 Token 是否即将过期。
    3. 如果接近过期时间,提前请求新的 Token。

优点

  • 减少因 Token 过期导致的中断操作。
  • 提高用户体验,避免频繁登录。

3. 滑动过期机制 (Sliding Expiration)

场景:用户频繁操作,Token 需要不断续期,但又不希望 Refresh Token 被频繁调用。

解决方案

  • 每次使用 Token 时,更新其过期时间。
  • 实现方式
    • 后端重新签发带有新过期时间的 Token。
    • 或通过 Redis 等缓存技术,更新 Token 的会话有效时间。
  • 示例逻辑
    1. 用户发送请求时,检查 Token 的剩余有效时间。
    2. 如果 Token 快要过期,重新签发 Token 并返回。
    3. 如果请求超时,Token 失效,用户需重新登录。

优点

  • 保持 Token 活跃性,避免频繁重新登录。
  • 安全性可控,可配合请求频率限制。

4. 单点登录 (SSO) 与全局注销

场景:用户通过单点登录访问多个系统,Token 在某一系统失效后需要同步更新。

解决方案

  • Token 版本控制
    • 每个用户记录一个全局版本号 (tokenVersion)。
    • Token 中包含版本号,验证时对比服务器记录。
    • 版本更新后,旧版本的 Token 即失效。
  • 集中 Token 存储
    • 使用 Redis 或数据库集中存储 Token。
    • Token 失效时,立即从存储中删除或标记为无效。

优点

  • 提高统一管理能力,支持多系统协同。
  • 实时注销功能提高安全性。

5. 异常处理机制

场景:用户因网络问题或操作不当导致 Token 失效,影响正常使用。

解决方案

  • 友好提示与自动重试
    • 前端检测到 Token 失效(如 401 状态码)后,提示用户重新登录或自动刷新 Token。
    • 示例:
      javascript
      axios.interceptors.response.use(
        (response) => response,
        async (error) => {
          if (error.response.status === 401) {
            // 自动刷新 Token
            const newToken = await refreshAccessToken();
            error.config.headers["Authorization"] = `Bearer ${newToken}`;
            return axios.request(error.config);
          }
          return Promise.reject(error);
        }
      );
  • 用户通知
    • 如果 Token 长时间未使用,主动向用户发送重新登录通知。

优点

  • 提高用户体验,减少因失效导致的困惑。

6. 备选方案:双 Token 验证

场景:高敏感操作需要额外验证。

解决方案

  • 结合 Access Token 和 CSRF Token:
    • 常规操作使用 Access Token。
    • 高敏感操作(如转账、密码修改)需额外验证 CSRF Token。
  • 强化请求签名机制,确保每次请求的唯一性。

总结:

根据业务需求选择合适的 Token 失效解决方案,推荐组合使用以下策略:

  1. 短期 Access Token + 长期 Refresh Token(基本方案)。
  2. 提前刷新机制(提升用户体验)。
  3. 滑动过期机制(适用于长时间操作)。
  4. 全局 Token 管理(适用于 SSO 场景)。

四、黑白名单方案

  • 黑名单方案:所有黑名单中的 JWT 将不可使用。
  • 白名单方案:不在白名单中的 JWT 将不可使用。

1. 黑名单方案

机制
将被禁止使用的 JWT 标记到黑名单中,无论其是否在有效期内,均拒绝该 Token 的访问请求。

实现方式

  1. 存储黑名单
    • 使用 Redis 或数据库存储黑名单中的 JWT 或其唯一标识(如 JTI)。
    • 每次请求时校验当前 JWT 是否在黑名单中。
  2. 加入黑名单的场景
    • 用户主动注销。
    • 系统检测到异常行为(如伪造、暴力请求等)。
    • Token 被标记为强制失效(如密码修改或权限调整)。
  3. 定期清理
    • 定时清理黑名单中过期的 JWT,以减少存储压力。

优点

  • 灵活性:允许根据需求动态使特定 JWT 失效。
  • 安全性:支持主动失效,防止已被盗用的 Token 被继续使用。

缺点

  • 额外存储开销:需要维护一份黑名单存储。
  • 性能消耗:每次请求需要检查 Token 是否在黑名单中。

适用场景

  • 用户可能主动注销,或需要强制下线的场景。
  • 支持动态控制和实时调整 Token 有效性的需求。

2. 白名单方案

机制
只有白名单中的 JWT 才被允许使用,未在白名单的 JWT 即使格式正确、未过期也将被拒绝。

实现方式

  1. 存储白名单
    • 将所有有效 JWT 或其唯一标识(如 JTI)存储在 Redis 或数据库中。
    • 在每次请求时验证当前 JWT 是否存在于白名单中。
  2. 白名单的更新
    • 用户登录成功后,将新生成的 JWT 添加到白名单。
    • 用户注销、超时或黑名单策略生效时,从白名单中移除相应的 JWT。
  3. 配合失效机制
    • 设置白名单中 JWT 的过期时间与其签发时的 exp 保持一致。
    • 失效后自动从白名单中清理。

优点

  • 更严格的控制:任何非白名单的 Token 都会被拒绝,即使其未被盗用或未过期。
  • 可实现全局注销:随时可以清空白名单,迫使所有用户重新登录。

缺点

  • 额外存储和性能开销:所有有效 Token 必须存储,尤其在高并发场景下存储量较大。
  • 依赖后端验证:JWT 的无状态特性被削弱,每次请求都需查询白名单。

适用场景

  • 高敏感操作或严格权限管理。
  • 需要更高安全性,要求任何异常 Token 都无法访问资源。

对比分析

特性黑名单方案白名单方案
存储需求仅存储被禁止的 JWT存储所有有效 JWT
性能消耗请求时校验是否在黑名单中请求时校验是否在白名单中
灵活性动态标记失效,易于扩展更严格控制,适用于高敏感场景
安全性基于已有 JWT 的额外保护所有 JWT 必须经过验证才能使用
适用场景用户主动注销或异常检测场景高敏感操作、严格权限管理场景

五、其他问题

如何基于 JWT 进行身份验证?

步骤如下:

  1. 用户向服务器发送用户名、密码、验证码等信息用于登录系统。
  2. 如果用户用户名、密码、验证码等信息校验正确的话,服务端会返回已经签名的 Token,也就是 JWT。
  3. 用户以后每次向后端发请求都在 Header 中带上这个 JWT 。
  4. 服务端检查 JWT 并从中获取用户相关信息。

两点建议:

  1. 建议将 JWT 存放在 localStorage 中,放在 Cookie 中会有 CSRF 风险。
  2. 请求服务端并携带 JWT 的常见做法是将其放在 HTTP Header 的 Authorization 字段中(Authorization: Bearer Token)。

如何防止 JWT 被篡改?

对于 JWT 而言,即使 JWT 被泄露或者捕获,黑客也没有办法篡改 TOKEN.因为服务端拿到了 JWT 会解析出其中的 Header、Payload、以及 Signature,通过密钥再次生成一个 Signature 与 JWT 中的 Signature 对比。所以黑客只要不知道我们的密钥是没有办法伪造 Token 的。

如何防止被抓包伪造获取 token?

1. 加密与验证机制
  • 使用 HTTPS:确保所有通信内容(包括 Token)经过加密传输。
  • Token 签名与验证:采用 JWT 等方式,对 Token 进行签名,并在服务器端验证签名的有效性。
  • Token 加密:对 Token 内容加密,确保即使被窃取也无法直接使用。
2. 限制 Token 使用范围
  • 短生命周期 Token:设置较短的有效期,并通过 Refresh Token 机制刷新。
  • 绑定客户端信息:将 Token 与 IP 地址、User-Agent 等绑定,防止跨设备使用。
  • 双 Token 验证:配合 CSRF Token 增强安全性。
3. 防止伪造请求与抓包
  • 请求签名机制:加入 timestampnonce,对请求生成唯一签名,服务端验证合法性。
  • 动态密钥与加密:在前后端通信中使用动态生成的密钥进行加密。
  • 前端防调试:混淆代码,检测调试工具,防止攻击者直接分析前端逻辑。
  • CORS 限制:配置服务器 CORS 策略,验证请求来源。
4. 用户行为与监控
  • 登录设备管理:记录 Token 使用的设备和 IP,异常时失效 Token。
  • 限速与防暴力破解:限制获取 Token 的接口调用频率,加入 CAPTCHA。
  • 实时监控与报警:监控异常 Token 活动,触发安全报警。

六、完整 JWT 示例

JWT 示例

plaintext
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJpc3MiOiJhdXRoLm15YXBwLmNvbSIsInN1YiI6IjEyMzQ1Njc4OTAiLCJhdWQiOiJteWFwcCIsImV4cCI6MTcxNjI0OTY5NiwiaWF0IjoxNzE2MjQ2MDk2LCJqdGkiOiJhYmNkMTIzNCIsIm5hbWUiOiJKb2huIERvZSIsImVtYWlsIjoiam9obi5kb2VAZXhhbXBsZS5jb20iLCJyb2xlIjoiYWRtaW4ifQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
  1. Header

    json
    {
      "alg": "HS256",
      "typ": "JWT"
    }
  2. Payload

    json
    {
      "iss": "auth.myapp.com",
      "sub": "1234567890",
      "aud": "myapp",
      "exp": 1716249696,
      "iat": 1716246096,
      "jti": "abcd1234",
      "name": "John Doe",
      "email": "john.doe@example.com",
      "role": "admin"
    }
  3. Signature
    一段由密钥生成的哈希值。