JWT 与会话身份验证
了解基于会话和 JWT 身份验证的区别。探索权衡、优势和使用案例,以为你的应用程序选择合适的身份验证方案。
一般来说,使用应用程序的第一步是身份验证,终端用户提供他们的身份凭证以成功登录。完成这一步之后,身份系统(即身份提供者、认证服务器等)就知道用户是谁以及他们可以访问哪些资源。
鉴于 HTTP 本质上是无状态的,会话中的每个请求都是独立的,并且不会回忆以前的信息。为每个操作重新验证用户既麻烦又不利于用户体验。
我们即将讨论 基于会话的身份验证 和 JWT(JSON Web Tokens)身份验证,这是两种用于保持身份验证状态的流行方法。每种方法都有其独特的优势和权衡,选择哪一种取决于你的应用程序的具体需求。如果你正在这两者之间抉择,本指南可以提供帮助。
什么是基于会话的身份验证?
基于会话的身份验证依赖于服务器来维护用户身份验证状态的记录。通过创建和管理会话,服务器让用户无需在每次请求时重新输入凭证即可保持登录并继续与应用程序交互。
基于会话的身份验证如何工作?
会话创建
- 用户进行身份验证并提供一些凭证(例如,电子邮件和密码)。
- 如果凭证有效,服务器会创建一个表示该会话的持久记录。会话包含信息,如随机字符串、用户标识符、会话开始时间、会话过期时间等。
SessionID
存储在数据库中,并以cookie 的形式返回给用户的客户端。
会话验证
- 该过程可以由用户手动触发(例如,点击标签、刷新页面),也可以由客户端自动触发(例如,在初始页面加载或通过带有
SessionID
的 API 调用时)。 - 每个后续调用都会从客户端发送一个带有会话 cookie 的 HTTP 请求到服务器。
- 服务器通过查询存储在服务器上的会话数据来验证
SessionID
。 - 如果有效,服务器处理请求并授权用户。
如何撤销会话?
会话可以实时失效,这在需要快速撤销访问权限的情况下非常有用。
- 用户手动注销:服务器删除会话记录,实际上就是注销用户。
- 管理员强制用户注销:管理员或系统可以通过从数据库中删除特定会话来终止它。例如,在安全漏洞期间。
- 会话过期:会话可以在设定的不活动时长后或固定时间限制后自动过期。
基于会话的身份验证的优势
- 简单可靠:会话记录提供了一个清晰的集中来源,允许高度的信任,并使授权决策更可靠。
- 实时撤销:通过删除或失效会话记录,用户的访问权限可以快速撤销。
基于会话的身份验证的劣势
- 分布式系统中的延迟:在多台服务器之间维护会话数据总是需要同步会话存储。这导致额外的延迟,因为服务器在每次请求时都要检查会话存储。
- 高资源消耗:每个会话都会占用服务器资源,当用户基数扩大时影响性能。
- 安全风险:会话劫持(通过被盗的会话 cookie)可能允许未经授权地访问用户账户。
- 对 API 的使用有限:基于会话的身份验证对于移动应用程序并不理想。它在服务器上存储会话数据,随着用户数量增加,会增加负担和复杂性。而且它使用 cookies,这在移动设备上处理起来更困难。
什么是 JWT 身份验证?
JSON Web Tokens (JWTs) 采取一种不同的方法,通过将所有相关的用户信息直接嵌入到令牌中,使用 JSON 对象。与基于会话的方法不同,JWT 是无状态的,这意味着服务器不管理身份验证记录。
JWT 身份验证如何工作?
一个 JWT 由三个部分组成:header、payload 和 signature。
- header 包含签名算法(例如,HS256)和令牌的类型(JWT)。
- payload 包含核心声明,例如用户的身份、用户角色和过期时间。
- signature 使用密钥对 header 和 payload 进行签名,允许验证签名是否被篡改。
JWT 发布
- 客户端将用户凭证发送到认证服务器(一个通用的身份提供者对于管理跨多个领域的访问特别有好处)。
- 认证成功后,服务器生成一个包含 header、payload 和 signature 的 JWT。
- AuthServer 向客户端发送已发布的令牌。客户端存储 JWT(例如,在 cookies、localStorage 或 sessionStorage 中)。
基于会话的工作流遵循相似的过程。然而,在身份验证之后,用户信息存储在服务器上的一个会话中,而 JWT 依赖发送给客户端的令牌进行存储和后续使用。
令牌验证
- 对于后续的 API 请求,客户端在
Authorization
头中发送 JWT(Bearer <token>
)。 - 服务器使用一个秘密或公钥验证 JWT 的签名并检查它的声明(例如,过期、发行者)。
- 如果令牌有效,服务器允许客户端访问请求的资源。
基于会话的身份验证需要服务器查询一个会话存储,尤其是如果它依赖于外部或集中式数据库时,这会很慢。相比之下,JWT 身份验证是无状态的,所有必要的信息都存储在客户端的令牌中,并利用签名确保安全性。这不再需要会话管理,尤其在分布式系统中更快速且更具扩展性。
如何撤销 JWT?
在客户端,注销通常意味着清除本地会话并从存储中移除令牌(ID、访问、刷新令牌)。然而,对于 JWT 身份验证,这仅仅是在本地签出用户,留下授权服务器上的集中式会话。因此,用户可能仍然能够访问同一会话下的其他应用程序,直到令牌过期或手动终止。
撤销 JWT 比基于会话的身份验证更具挑战性,因为 JWT 是无状态的,且一旦发布就无法失效,除非实施特定策略。常用的方法包括:
- 短期过期时间:为 JWT 设置一个短期
exp
声明(例如,15 分钟)。过期后,用户必须重新认证。这可以最大限度地减小令牌被盗的风险,因为攻击者只能在有限时间内使用它。为保持流畅的用户体验,刷新令牌 可用于降低重新认证的不便。 - 令牌黑名单:对于关键情况(例如,用户注销、密码更改),维护一个撤销令牌的黑名单。服务器检查传入令牌与此黑名单的匹配情况,拒绝任何匹配项。虽然有效,但这种方法需要跟踪撤销的令牌,这违反了 JWT 的无状态性质,并且如果列表增长过大,可能效率较低。
- 撤销端点:在授权服务器上引入一个撤销端点,在此令牌(例如,刷新令牌)可以被失效。一旦刷新令牌被撤销,由它发布的任何访问令牌都将无法再续签。这种方法在 OAuth2 流程中效果很好。
JWT 身份验证的优势
- 快速且信息丰富:JWT 的自包含性质使得客户端验证更快且更高效,无需服务器交互。它们还可以在令牌中包含自定义声明(例如,用户角色或其他相关数据),使得服务器可以不必查询数据库即可决定角色。
- 增强的安全性:JWT 使用签名和加密技术,使攻击更难。
- 跨域支持:JWT 非常适合单点登录 (SSO)和跨域身份验证。它们允许用户用同一令牌在多个域或服务之间进行身份认证。
- 移动友好:JWT 非常适合需要无状态身份验证的移动应用。令牌可以在客户端进行存储,并随每个请求发送,提高了效率和易用性。
JWT 身份验证的劣势
-
JWT 不是实时更新的
一旦 JWT 被签发,就无法撤销或更新,并且只要签名有效且未过期,它就被认为是有效的。
如果用户的访问权限发生变化(通常是被降低),用户仍然能够访问已经移除的资源,直到 JWT 过期。同样地,如果 JWT 包含基于角色的授权信息,新的授权范围在旧的 JWT 过期之前不会生效。换句话说,JWT 不适用于实时撤销,用户可以设置适当的过期时间来缓解这个问题。
-
多设备和撤销困境
不可能在 JWT 过期前验证所有已发行的 JWT 以实现用户在所有设备上撤销。虽然理论上可以撤销签名密钥以使 JWT 失效,但这也将使使用该密钥的所有 JWT 失效,并且处理缓存密钥的过程使得这种方法对于简单的用户撤销操作来说不可行。
一些身份提供者可能已经为这些 JWT 问题提供了现成的 解决方案。有关更多信息,请查看“改善 JWT 身份验证体验的最佳实践”。
JWT 和会话有什么区别?
会话和 JWT 是两种在无状态 HTTP 世界中持久化身份认证和授权上下文的流行方法。尽管这两种方法各有优劣,但它们提供了不同的收益和缺点。
会话,提供了更强的单个请求授权保证,并且更易于安全实施。然而,它们依赖于服务器端数据库验证引入了延迟开销,这可能对高响应性应用程序的用户体验产生负面影响。
另一方面,JWT 在更快的授权和与外部应用程序的互操作性方面具有优势,但需要更多的开发人员努力来解决安全的复杂性。例如,我们可以使用 webhooks 在用户的访问被撤销时通知客户端,以便客户端可以清除缓存的 JWT 并强制用户重新认证。
由于基于令牌的身份验证更适合于可扩展性,尽管其缺点仍然可控,越来越多的现代应用程序正在采用它。
会话与 JWT:选择合适的方法
你的身份验证方法应与应用程序的架构和特定需求相匹配。以下是一个快速指南,帮助你决定:
何时使用基于会话的身份验证
基于会话的身份验证最适合需要实时会话控制、需要集中管理或可扩展性不是主要关切的情况。以下是其优势所在:
-
具有持久会话的 Web 应用程序
对于在线购物网站这 样的平台,会话对于在用户访问期间跟踪用户、购物车和偏好设置至关重要。
-
需要实时会话控制的应用程序
银行或金融服务等应用程序受益于服务器控制的会话数据,确保强大的访问管理和安全性。
-
单服务器或小规模系统
不需要大量扩展的小规模内部工具或应用程序依赖简单的会话管理以提升易用性和可靠性。
何时使用 JWT 身份验证
JWT 身份验证更适合优先考虑可扩展性、效率和分布式系统的应用程序。它特别适用于客户端和服务器之间的无状态交互。考虑在以下情况下使用基于令牌的身份验证:
-
单点登录 (SSO)
JWT 完美适用于单点登录,允许用户仅进行一次身份验证并使用同一个令牌无缝访问多个服务或应用。分享关于通过 OAuth 2.0 和 OIDC 安全云应用的详细解释,提供使用 JWT 格式的访问令牌和ID 令牌的例子。
-
移动应用程序
移动应用通常更喜欢使用 JWT 进行身份验证,因为令牌可以在设备上安全存储,并随每个 API 请求发送。探索Android / iOS 的 JWT 身份验证快速集成。
-
微服务架构
在微服务环境中,JWT 允许每个服务独立验证令牌,无需依赖于集中会话存储,从而确保可扩展性和效率。
-
跨域身份验证
在涉及多个域或子域(例如
api.example.com
、dashboard.example.com
和docs.example.com
)的场景中,JWT 表现突出。不像 cookies,JWT 允许跨域身份验证而无需附加依赖。 -
API 和 Web 服务
RESTful APIs 和 Web 服务通常使用 JWT 进行身份验证,因为它们轻量、便携,并消除了服务器端会话管理的需要。了解机器间身份验证的更多信息,以应对你的应用程序需要直接与资源通信的场景。
改善 JWT 身份验证体验的最佳实践
JWT 身份验证是一个很好的工具,但它可能会带来影响用户体验的挑战。Logto 提供了一种简单可靠的解决方案来克服这些障碍,使其成为安全高效身份验证的首选。
处理 JWT 的用户注销问题
JWT 身份验证中的一个常见问题是确保适当的用户注销体验。Logto 通过其开箱即用的 SDK 简化了这个过程。
- 通过在客户端清除令牌和本地会话并将用户重定向到 Logto 的结束会话端点,你可以轻松终止客户端应用程序和服务器上的会话。
- 此外,Logto 支持后端通道注销,允许 AuthServer 在用户签出时通知所有共享相同会话的客户端应用程序。
这确保了在你的生态系统中一致和安全的会 话管理。了解更多有关处理注销的信息。
处理用户权限更改
管理 JWT 实现用户权限的实时更改也可能很棘手。由于 JWT 本质上是无状态的,任何更新的权限或角色在令牌过期之前可能不会生效。Logto 提供了有效处理此问题的策略:
- 对于减少该用户的权限:使用短期访问令牌过期时间或通过 API 调用动态验证权限。
- 对于添加该用户的权限:更新 AuthServer 以包含新的权限范围,并让用户重新同意应用这些更改。
这些解决方案有助于保持权限的更新并确保更安全、更灵活的系统。了解更多关于处理权限更改的信息。