JWT双令牌(双token)实现登录验证
yaoye Lv5

处理 JWT 的过期机制并结合刷新 Token 实现无缝登录是一个常见的需求。为了防止用户频繁登录,同时保持安全性,通常会使用短期有效的访问 Token(Access Token)和长期有效的刷新 Token(Refresh Token)来平衡两者。下面将逐步介绍如何实现这一机制。

访问 Token 和刷新 Token 的基本概念

  • **访问 Token (Access Token)**:用于验证用户的身份,并授予访问资源的权限。它通常短期有效(如 15 分钟至 1 小时),以减少长期暴露的安全风险。

  • **刷新 Token (Refresh Token)**:用于在访问 Token 过期后,获取新的访问 Token。它具有较长的有效期(如几天或几周),并仅在服务端存储或安全地处理,用户无法直接使用它来访问资源。

基本流程

  1. 用户登录:用户通过用户名和密码或其他认证方式登录,服务器验证通过后,生成并返回一个 访问 Token 和一个 刷新 Token
  2. 使用访问 Token 访问资源:在访问受保护的资源时,用户每次请求都需要携带访问 Token(通常放在 HTTP Header 中)。
  3. 访问 Token 过期后刷新:当访问 Token 过期时,前端通过携带刷新 Token 向后端请求新的访问 Token,而无需再次登录。
  4. 刷新 Token 过期后重新登录:如果刷新 Token 也过期,则需要用户重新登录。

刷新 Token 的安全性

  1. 刷新 Token 存储:刷新 Token 不应该暴露在前端的存储中。建议存储在 HttpOnly Cookie 中,这样客户端无法通过 JavaScript 访问刷新 Token,减少了被攻击者盗取的可能。
  2. 刷新 Token 的双 Token 校验:为了进一步增强安全性,服务端可以在数据库中记录刷新 Token 的状态,并且要求前端每次刷新时提供当前 Token 和设备标识等信息。
  3. 刷新 Token 黑名单机制:当用户登出或刷新 Token 被滥用时,可以将对应的刷新 Token 标记为无效,避免被盗用。

完整的流程图

graph TD
    A[用户登录] --> B[返回 Access Token 和 Refresh Token]
    B --> C[用户使用 Access Token 访问资源]
    C --> D{Access Token 是否过期?}
    D -- 否 --> C
    D -- 是 --> E[请求刷新 Token]
    E --> F[使用 Refresh Token 获取新 Access Token]
    F --> C
    F --> G{Refresh Token 是否过期?}
    G -- 否 --> F
    G -- 是 --> H[用户重新登录]
    C --> I[登出]
    I --> J[使 Refresh Token 失效]

注意事项

  • 短期 Token 的有效期:建议将访问 Token 的有效期设定得较短,例如 15-60 分钟,这样可以减少长期有效的 Token 被盗用后造成的风险。
  • 刷新 Token 的有效期:刷新 Token 可以设为几天到几周,但也需要根据安全策略来定期更新或使其失效。
  • 防止 Replay 攻击:刷新 Token 建议只使用一次,刷新 Token 后应颁发新的刷新 Token,旧的应作废。

双token怎么处理用户权限改变

将权限信息(包括限时权限的有效期)直接写入 JWT 中,然后在权限提升或变更时,用户通过刷新 Token 来重新申请授权。这样,大多数请求不需要频繁查询权限数据库,同时通过刷新机制保证了权限变更后的有效性。

将权限信息嵌入 JWT

在 Token 中直接嵌入用户的权限信息和临时权限的有效期,JWT 可以包含以下几个关键字段:

  • 用户基本信息:如 user_idusername 等。
  • 权限信息:用户所拥有的所有权限(包括普通权限和临时权限)。
  • 权限有效期:针对临时权限,可以在 JWT 中嵌入其具体的过期时间。
  • **签发时间(iat)**和**过期时间(exp)**:JWT 标准的签发时间和有效期,用于 Token 的生命周期管理。

这样,每次用户发送请求时,微服务或网关只需解析 JWT 即可校验用户的权限,无需频繁查询数据库,极大提高了性能。

权限提升时刷新 Token

当用户权限发生提升变更时,可以触发 Token 刷新机制:

  • 当用户获得新的权限(例如更高级别的访问权限),需要刷新 Token 以将这些新权限包含在新的授权 Token 中。
  • 可以通过两种方式触发 Token 刷新:
    • 自动刷新:当权限提升时,系统通过某种机制(如消息队列或后台作业)通知客户端刷新 Token。
    • 手动刷新:用户主动触发刷新 Token 的请求,例如通过应用界面的提示,或者当某些高级功能需要新权限时,用户会被要求重新登录或刷新 Token。

JWT 的过期时间与临时权限控制

  • 对于普通的长期权限,JWT 中可以直接记录用户的权限列表。
  • 对于临时权限,JWT 中可以设置一个权限的过期时间(比如一个 expires_at 字段),表示该权限在某个时间点之后会失效。通过这种方式,系统可以自动在 Token 的有效期内检查和控制临时权限的有效性。
  • 细粒度的权限管理:通过将临时权限的有效期嵌入 Token,微服务在每次请求时只需要解析 Token 中的权限和有效期,判断当前时间是否超过权限的有效期,从而动态管理权限的生效和失效。

QA

Q: 既然刷新 Token 建议只使用一次,那为什么不抛弃刷新 Token 只使用短期 Token 呢

在设计使用 双 Token 机制 时,使用 短期访问 Token (Access Token)单次有效的刷新 Token (Refresh Token) 有其独特的优势。让我们从以下几个角度来解释为什么不完全依赖短期 Token,而是采用双 Token 机制。

  • 用户体验 vs 安全性

    • 单独使用短期 Token:如果你只使用短期有效的 Token(比如每 15 分钟过期),那么在每个 Token 过期时,用户都必须重新登录以获取新的 Token。这会极大影响用户体验,特别是在用户长时间使用系统的场景下,如访问一个长时间未操作的页面,Token 过期后强制登录会打断用户的使用流程。
    • 双 Token 机制:通过使用一个长期有效的刷新 Token,用户无需频繁登录。刷新 Token 可以在访问 Token 过期时,自动为用户生成新的访问 Token,而用户在前端并不会感知到这个过程,从而实现 无缝登录
  • 防止 Replay 攻击的必要性

    • Replay 攻击 是指攻击者截获并重复使用用户的认证信息(如 Token)以非法访问系统。在双 Token 机制下,如果刷新 Token 是多次有效的,一旦被攻击者截获,他们就可以反复使用该刷新 Token 来生成新的访问 Token,从而长期保持对系统的访问权限。
    • 单次使用的刷新 Token:为防止这种情况,采用单次有效的刷新 Token 策略,即每当客户端成功使用刷新 Token 获取新的访问 Token 后,服务端会生成一个新的刷新 Token,并废弃旧的刷新 Token。这样,即便攻击者截获了旧的刷新 Token,由于它已经被标记为无效,无法再用来生成新的访问 Token。
  • 为什么不直接只用短期 Token?

    假设我们完全抛弃刷新 Token,而只依赖短期访问 Token,这会带来几个问题:

    • 用户频繁登录:短期 Token 的过期时间通常较短(如 15 分钟)。如果只使用短期 Token,一旦 Token 过期,用户就必须重新登录系统。对于用户来说,这是一个不便和负担。
    • 安全性与用户体验的平衡:双 Token 机制允许我们通过短期的访问 Token 来限制 Token 暴露的风险,同时使用较长期的刷新 Token 来避免频繁登录。这样,访问 Token 可以频繁更换,提高安全性,而刷新 Token 则在必要时更新,提供一种平衡机制。
  • 双 Token 机制的流程

    假设我们启用了 单次有效的刷新 Token 策略,整个认证过程可以是这样的:

    • 用户通过用户名和密码登录系统,服务器返回一个短期的 访问 Token 和一个长期的 刷新 Token
    • 用户使用访问 Token 访问资源,当访问 Token 过期时,前端会自动使用刷新 Token 请求新的访问 Token。
    • 在使用刷新 Token 成功获取新的访问 Token 后,服务器返回一个新的刷新 Token,并使旧的刷新 Token 无效。
    • 如果刷新 Token 也过期或被攻击者滥用,用户需要重新登录。

    这样,每次刷新操作都会更新刷新 Token,同时旧的 Token 失效。即使攻击者截获了之前的 Token,它们也只能使用一次,并且无法长期滥用。

  • 使用双 Token 机制的优势

    • 减少频繁登录:用户体验更好,避免频繁要求用户重新登录。
    • 提升安全性:通过短期的访问 Token 限制 Token 被盗用的时间窗口,同时通过刷新 Token 的单次使用机制防止 Replay 攻击。
    • 防止长期滥用:即使攻击者截获了某个刷新 Token,由于它是单次有效的,攻击者无法通过重复使用刷新 Token 长期滥用系统。

总结

双 Token 机制通过结合短期访问 Token 和单次使用的刷新 Token 提供了一个平衡的方案,既能提高用户体验,又能保证安全性。虽然刷新 Token 建议只使用一次,但它的存在是为了在保证安全的前提下减少用户的登录次数,而不是完全抛弃它。

如何进行用户的权限验证

在双 Token 机制下进行用户的权限验证,需要结合访问 Token (Access Token)刷新 Token (Refresh Token) 来确保安全性和高效性。以下是详细的解释:

  1. 双 Token 的角色

    • **访问 Token (Access Token)**:主要用于快速鉴权和授权,包含用户的基本身份信息和权限信息。通常是短期有效的,例如 15 分钟或 30 分钟。
    • **刷新 Token (Refresh Token)**:用于在访问 Token 过期时,生成新的访问 Token。它不包含用户的权限信息,通常是长期有效的,但需要在某些情况下失效(如用户登出、修改密码等)。

    双 Token 机制下,用户的权限验证主要通过访问 Token进行,而刷新 Token只负责获取新的访问 Token,不直接用于权限验证。

  2. 权限验证流程

    在双 Token 机制下,用户权限验证的典型流程如下:

    1. 用户登录,服务器生成两个 Token:
      • 一个短期的访问 Token,包含用户的身份信息和权限信息。
      • 一个长期的刷新 Token,用于在访问 Token 失效时获取新的访问 Token。
    2. 用户携带访问 Token 访问受保护的资源:
      • 用户在每次请求时,将访问 Token 通过 HTTP 头(通常是 Authorization: Bearer <access_token>)传递给服务器。
      • 服务器在收到请求后,验证访问 Token 的签名、是否过期、以及其包含的权限信息(例如用户的角色、具体权限等)。
    3. 服务器验证 Token 并授权:
      • 服务器解析访问 Token,验证其中包含的用户身份和权限信息。
      • 根据解析的权限信息,决定用户是否有权执行当前操作。如果权限验证通过,服务器继续处理业务逻辑,否则返回 403(Forbidden)。
    4. 访问 Token 过期:
      • 如果访问 Token 过期,前端会携带刷新 Token 访问专门的刷新接口,服务器验证刷新 Token 的有效性后,重新颁发新的访问 Token 和刷新 Token。
      • 刷新后的访问 Token 中会包含更新的权限信息,确保用户的权限是最新的。
    5. 权限信息更新:
      • 如果用户的角色或权限发生变化(例如用户被赋予新权限,或权限被收回),刷新 Token 可以生成新的访问 Token,并将最新的权限信息带入新的访问 Token 中。
      • 对于长时间使用的用户,权限变化可能不会立即反映在当前的访问 Token 上,但当下一个访问 Token 被刷新时,新权限将生效。
  3. 双 Token 机制下的权限验证策略

    1. Token 中包含权限信息

      访问 Token 通常是自包含的,也就是说,所有需要的信息都嵌入在 Token 内,包括用户身份、角色、权限等。这可以通过 JWT 的 payload 部分来实现,典型的 JWT 可能包含如下信息:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      {
      "sub": "user123", // 用户 ID
      "role": "admin", // 用户角色
      "permissions": [ // 权限信息
      "read:data",
      "write:data"
      ],
      "exp": 1700000000 // 过期时间
      }

      服务器只需要解析访问 Token 的 payload,直接从中获取用户的权限信息,判断是否允许访问特定资源。

    2. 在数据库中存储权限信息

      虽然访问 Token 中可以包含权限信息,但这些权限可能会随时间动态变化。例如,用户的角色被调整,或者新的权限被赋予用户。因此,部分系统设计中,Token 中只包含用户的身份信息(如用户 ID),权限信息则在每次请求时,从数据库或权限管理系统中动态查询。

      • 优点:实时性更强,用户权限的任何变更都能立即生效,尤其适合那些权限经常调整的系统。
      • 缺点:需要增加额外的数据库查询,可能会对性能造成影响,特别是在高并发场景下。
    3. 混合策略

      一种常见的策略是使用混合方式:

      • Token 中包含基本权限信息:如用户的角色和基础权限。
      • 数据库查询进行细粒度权限验证:对于特定操作,如果权限要求较为复杂或动态调整较多,可以在访问时,从数据库中查询细粒度的权限信息。

      这种方式能够平衡性能和实时性需求。

    4. 权限验证中的特殊处理场景

      1. 刷新 Token 被盗用

      如果刷新 Token 被攻击者获取,攻击者可以使用它不断刷新新的访问 Token,进而长期访问系统。因此,以下几种方式可以提高安全性:

      • 设置刷新 Token 的过期时间:虽然刷新 Token 通常比访问 Token 有更长的有效期,但它也应有一定的过期时间。
      • 单次有效刷新 Token:每次使用刷新 Token 后,服务器生成新的刷新 Token,并废弃旧的刷新 Token,避免被截获后重复使用。
      • IP 地址或设备绑定:在颁发刷新 Token 时,记录用户的 IP 地址或设备信息,刷新时验证这些信息是否一致。
      1. Token 黑名单机制

      当用户主动登出、权限被撤销或刷新 Token 被发现泄露时,服务器可以将该 Token 加入黑名单,即使它在有效期内也无法再使用。

      • 在访问时,系统检查该 Token 是否在黑名单内,如果在,则拒绝访问。
      1. 权限变更时的处理
      • 实时更新:如果用户的权限在系统中发生变化,可以通过某种方式(如在刷新 Token 时)通知客户端获取新的访问 Token,以反映最新的权限信息。
      • 批量失效:系统可以批量失效与某个用户相关的所有 Token(访问 Token 和刷新 Token),强制用户重新登录以更新权限。
    5. 权限验证流程总结

      1. 初次登录时,服务器颁发访问 Token刷新 Token,访问 Token 包含用户的身份信息和权限信息。
      2. 用户请求资源时,携带访问 Token,服务器验证 Token 的有效性和权限信息。
      3. 访问 Token 过期后,前端携带刷新 Token 向服务器请求新的访问 Token,并更新权限信息。
      4. 权限变更时,新的权限信息会在下一次刷新 Token 时反映在新的访问 Token 中,确保权限同步。
      5. 如果用户的 Token 失效或被攻击者滥用,可以通过黑名单机制或单次刷新机制确保系统安全。