JWT的最佳实践

前言
在当今的 web 开发中,身份验证是非常关键的,而 JWT(JSON Web Token)作为一种简单、高效的身份验证方式,已经被广泛应用在许多前后端分离的项目中。本篇文章将带你从零开始理解 JWT,探讨它的优缺点,并介绍最佳实践。


什么是 JWT?

JWT 是一种开放标准(RFC 7519),用于在不同系统之间安全地传递信息。简单来说,JWT 主要用于认证和信息交换,常见的应用场景就是用户登录验证。JWT 通常在用户登录后生成,并随后的请求中带着它来进行身份认证。

JWT 结构

JWT 的结构非常简单,它由三部分组成:

  1. 头部(Header)
    头部通常包含两部分内容:令牌类型(JWT)和签名算法(如 HMAC SHA256 或 RSA)。

  2. 载荷(Payload)
    载荷是 JWT 的“正文”部分,存储了你希望传递的数据(比如用户 ID 或权限)。这里面包含了 “Claims”(声明),有三种类型:

    • 注册声明:如 sub(主题)、exp(过期时间)、iat(签发时间)等。
    • 公共声明:可以自定义的数据,但需要避免冲突。
    • 私有声明:你自己定义的声明。
  3. 签名(Signature)
    签名部分用于验证信息的完整性,确保数据没有被篡改。生成签名的方式是:用头部和载荷,通过密钥(对于对称加密是一个密钥,非对称加密是私钥)生成。


JWT 的生成和验证原理

JWT 的生成过程

  1. 用户登录
    用户输入用户名和密码,通过认证系统登录。

  2. 服务器生成 JWT
    认证成功后,服务器会使用一个密钥(对称加密)或者私钥(非对称加密)生成一个 JWT。生成过程包括:

    • 将头部和载荷(即数据)部分通过 Base64 编码。
    • 用密钥对这两部分进行签名,生成签名部分。

    生成后的 JWT 看起来像这样:

    header.payload.signature
    

    例如:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    

    jwt构成

  3. 返回 JWT 给客户端
    服务器把生成的 JWT 返回给客户端,通常客户端会将其存储在本地(如 LocalStorage 或 Cookie)中。

JWT 的验证过程

  1. 客户端发起请求
    客户端每次请求时,会将 JWT 添加到请求的 Authorization 头部中(例如:Authorization: Bearer <JWT>)。

  2. 服务器验证 JWT
    服务器拿到 JWT 后,通过以下步骤来验证:

    • 解析 JWT 的头部、载荷和签名。
    • 使用密钥(对于对称加密)或公钥(对于非对称加密)对签名部分进行验证,看是否与头部和载荷部分匹配。

    如果验证通过,说明 JWT 的数据未被篡改,服务器就会继续处理请求。如果验证失败,说明 JWT 被篡改,服务器会返回错误信息。


JWT 的优点

  1. 无状态(Stateless)
    JWT 是无状态的,意味着服务器不需要存储用户会话数据。每次请求都包含 JWT,服务器可以根据 JWT 里的信息来进行认证。这样减少了服务器的负担,提升了系统的扩展性。

  2. 跨域支持
    由于 JWT 是由客户端传递,它非常适合前后端分离的项目,可以跨域进行身份验证,避免了传统 Cookie 的同源限制。

  3. 轻量级
    JWT 的大小较小,通常在几十到一百多个字节之间,非常适合频繁的 HTTP 请求,传输效率高。

  4. 适合微服务架构
    JWT 可以在多个服务之间传递身份信息,非常适合分布式架构,尤其是微服务架构中的身份认证。


JWT 的缺点

  1. 无法撤销
    一旦 JWT 被发放出去,它就无法撤销。如果 JWT 泄露,攻击者可以在有效期内反复使用。这比传统的 Session 认证要危险,后者可以在服务器端主动注销会话。

  2. 存储和安全问题
    JWT 如果存储不当(如保存在 LocalStorage 或 Cookie 中),可能会受到 XSS 攻击。如果攻击者能够获取到 JWT,就能伪造合法请求。

  3. 过期问题
    JWT 通常设定有过期时间,一旦过期,就不能再使用。为了避免频繁登录,可以使用刷新令牌(Refresh Token)。不过如果过期时间设置不合理,可能会影响用户体验。

  4. 载荷不加密
    JWT 的载荷部分是 Base64 编码的,不是加密的。任何人都可以解码并看到其中的内容。因此,切勿将敏感信息(如密码)存储在 JWT 中。

  5. 密钥泄漏的风险
    JWT 的安全性依赖于密钥的保密性。如果密钥泄露,攻击者可以伪造有效的 JWT。因此,保护好密钥非常重要。


JWT 的使用场景

  1. 单点登录(SSO)
    JWT 非常适合实现跨应用的单点登录(SSO)。只需生成一次 JWT,就可以在多个系统之间共享。

  2. 移动应用身份认证
    在移动应用中,JWT 常常用来进行身份验证。客户端存储 JWT,并在每次请求中发送。

  3. 前后端分离的应用
    在前后端分离的架构中,前端和后端可以独立进行身份验证,使用 JWT 作为认证工具,避免了传统 Cookie 的问题。


JWT 的最佳实践

  1. 不要在 JWT 中存储敏感信息
    由于 JWT 的载荷部分是可以解码的,敏感信息(如密码、银行卡信息)绝对不能存储在 JWT 中。你可以把这些信息存储在服务器端,JWT 只传递一个标识符。

  2. 使用 HTTPS
    无论是生成 JWT 还是传输 JWT,都应使用 HTTPS 协议,确保数据在传输过程中不会被窃听或篡改。

  3. 设置合理的过期时间
    JWT 的过期时间要设置合理。过短可能影响用户体验,过长则可能存在安全隐患。通常,访问令牌的过期时间可以设置为 15 分钟到 1 小时,刷新令牌可以设置较长时间。

  4. 使用刷新令牌
    结合使用访问令牌(Access Token)和刷新令牌(Refresh Token)。当访问令牌过期时,客户端可以使用刷新令牌获取新的访问令牌。

  5. 存储 JWT 的安全性
    如果你把 JWT 存储在浏览器中,推荐使用 HttpOnly 和 Secure 标志的 Cookie,以避免 XSS 攻击的风险。避免将 JWT 存储在 LocalStorage 中,因为它更容易受到 XSS 攻击。

  6. 签名算法选择
    在选择签名算法时,推荐使用非对称加密的算法(如 RS256 或 ES256),而不是对称加密的算法(如 HS256),这样可以提高安全性,避免密钥泄露风险。

  7. 密钥管理
    密钥是 JWT 安全的关键。无论是对称加密还是非对称加密,都应该采取安全措施保护密钥。避免将密钥硬编码在代码中,可以通过环境变量或专门的密钥管理工具来管理密钥。


总结

JWT 是一种非常流行的身份验证方式,它简单、轻量、无状态,非常适合现代 web 应用,尤其是前后端分离的项目。但它也有一些缺点,尤其是在密钥泄漏和无法撤销的场景下。因此,使用 JWT 时需要特别注意密钥的管理和安全配置,合理设置过期时间,避免将敏感信息存储在 JWT 中。