Skip to content

JWT介绍

JWT (JSON Web Token) 是目前最流行的跨域认证解决方案之一,它是一个开放标准 (RFC 7519),定义了一种紧凑且自包含的方式,用于在各方之间安全地传输信息。 由于信息是经过数字签名的,所以可以被验证和信任。

JWT 的核心优势在于其无状态性。在传统的基于 Session 的认证中,服务器需要存储用户的会话信息。 而 JWT 自身就包含了所有需要验证的信息,服务器无需在后端保留任何用户状态,这极大地减轻了服务器的压力,并提高了系统的可伸缩性和可用性。

JWT 的核心结构

一个 JWT 本质上是一个由点 (.) 分隔的长字符串,这个字符串由三部分经过 Base64Url 编码后连接而成:

Header.Payload.Signature

一个典型的 JWT 如下所示: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

下面我们来逐一解析这三个部分:

  1. 头部 (Header) 头部通常由两部分组成:令牌的类型 (typ),固定为 "JWT";以及所使用的签名算法 (alg),例如 HMAC SHA256 (HS256) 或 RSA (RS256)。 json { "alg": "HS256", "typ": "JWT" } 这个 JSON 对象会经过 Base64Url 编码,构成 JWT 的第一部分。

  2. 载荷 (Payload) 载荷部分包含了“声明 (Claims)”,这些是关于实体(通常是用户)和其他附加元数据的陈述。 声明分为三种类型:

    • 注册声明 (Registered Claims):这是一组预定义的、非强制但建议使用的声明,以提供一组有用的、可互操作的声明。 常见的有:
      • iss (Issuer): 令牌的签发者。
      • sub (Subject): 令牌的主题,通常是用户的唯一标识符。
      • aud (Audience): 令牌的接收者。
      • exp (Expiration Time): 令牌的过期时间戳。
      • iat (Issued At): 令牌的签发时间。
      • nbf (Not Before): 定义在此时间之前,令牌不可用。
      • jti (JWT ID): 令牌的唯一标识符。
    • 公共声明 (Public Claims):可以由使用者自由定义,但为了避免冲突,应该在 IANA JSON Web Token Registry 中注册或使用包含抗冲突命名空间的 URI。
    • 私有声明 (Private Claims):这是在通信双方之间自定义的声明,用于共享信息。

    重要提示:载荷部分同样是经过 Base64Url 编码的,这意味着它可以被轻易地解码和读取。 因此,绝对不能在载荷中存放任何敏感信息,如密码

  3. 签名 (Signature) 签名的作用是保证 JWT 的完整性和真实性,防止数据在传输过程中被篡改。 它的生成过程如下:

    1. 获取经过 Base64Url 编码的头部。
    2. 获取经过 Base64Url 编码的载荷。
    3. 将两者用点 (.) 连接起来。
    4. 使用头部 (alg 字段) 中指定的签名算法,配合一个只有服务器知道的密钥 (secret),对连接后的字符串进行签名。

    例如,使用 HS256 算法的签名伪代码为: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

JWT 的工作与认证流程

JWT 主要应用于身份验证和信息交换。 其典型的认证流程如下:

  1. 用户登录:用户使用其凭据(如用户名和密码)向服务器发起登录请求。
  2. 令牌生成与分发:服务器验证用户凭据。如果成功,服务器会创建一个 JWT,其中包含用户的标识和其他必要信息,然后通过响应将其发送回客户端。
  3. 客户端存储:客户端收到 JWT 后,通常会将其存储在本地,例如浏览器的 Local Storage、Session Storage 或一个安全的 HttpOnly Cookie 中。
  4. 授权请求:当客户端需要访问受保护的 API 或资源时,它会在请求的 Authorization 头中携带 JWT,通常使用 Bearer 模式。 Authorization: Bearer <token>
  5. 服务器验证:服务器在收到请求后,会从请求头中提取 JWT。接着,服务器会使用自己的密钥来验证令牌的签名。如果签名有效,服务器就可以信任该令牌中的信息,并根据其中的声明(如用户 ID、权限等)来授权用户的请求。
  6. 响应资源:一旦令牌验证成功,服务器就会处理该请求并返回相应的资源或数据。

如何保证 JWT 的安全

JWT 的安全性依赖于其签名机制和一系列安全最佳实践的结合:

  • 核心安全机制:数字签名:签名确保了 JWT 在传输过程中没有被篡改(数据完整性),并且可以验证其签发者(认证与防伪)。 攻击者因为没有服务器的密钥,无法伪造出有效的签名。
  • 始终使用 HTTPS:为了防止中间人攻击和令牌窃听,JWT 必须通过加密的 HTTPS 连接进行传输。
  • 选择强签名算法:避免使用 none 算法,并优先选择如 RS256(非对称)或 HS256(对称)等强加密算法。服务器应强制校验算法,不接受客户端指定的算法。
  • 密钥管理:签名密钥必须被严格保密且足够复杂。定期轮换密钥是一个很好的安全习惯。
  • 设置合理的过期时间 (exp):为令牌设置一个较短的有效期,可以大大降低令牌被盗用后所带来的风险。
  • 刷新令牌机制 (Refresh Token):为了平衡安全性和用户体验,通常会使用两种令牌:
    • 访问令牌 (Access Token):生命周期短(如15分钟),用于访问资源。
    • 刷新令牌 (Refresh Token):生命周期长(如7天),用于在访问令牌过期后获取新的访问令牌。
  • 不在载荷中存放敏感数据:再次强调,JWT 的载荷是公开可读的,切勿存放密码、个人身份信息等敏感数据。
  • 客户端安全存储:在 Web 应用中,将 JWT 存储在 HttpOnly 类型的 Cookie 中可以有效防止 XSS (跨站脚本) 攻击窃取令牌。