JWT 是什么

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用环境中以紧凑的方式安全地传递信息。JWT 可以用于身份验证和信息交换,因其结构简单、易于使用而广泛应用于现代 Web 应用程序中。

简单理解下,JWT 就是用一种结构化封装的方式来生成 token 的技术。结构化后的 token 可以被赋予非常丰富的含义,这也是它与原先毫无意义的、随机的字符串形式 token 的最大区别。

结构化之后,令牌本身就可以被“塞进”一些有用的信息,比如软件的授权的信息、授权的范围信息等。或者,你可以形象地将其理解为这是一种“自编码”的能力,而这些恰恰是无结构化令牌所不具备的。

JWT 的结构

JWT 这种结构化体可以分为 HEADER(头部)、PAYLOAD(数据体)和 SIGNATURE(签名)三部分。经过签名之后的 JWT 的整体结构,是被句点符号分割的三段内容,结构为 header.payload.signature 。

  1. Header(头部):通常包含两部分信息:令牌的类型(通常是 “JWT”)和所使用的签名算法(如 HMAC SHA256 或 RSA)。
{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload(数据体)

私有声明:双方自定义的声明,通常用于特定应用。

包含要传递的数据,称为声明(claims)。声明可以是关于用户的信息(如用户 ID、角色等)或其他元数据。

声明分为三类:

注册声明:预定义的声明,如 iss(发行者)、exp(过期时间)、sub(主题)等。

公共声明:可以自定义的声明,需避免与其他人冲突。

私有声明:双方自定义的声明,通常用于特定应用。

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "exp": 时间戳加上有效时间,比如+1day

}

更多的通用声明,你可以参考RFC 7519 开放标准。不过,在一个 JWT 内可以包含一切合法的 JSON 格式的数据,也就是说,PAYLOAD 表示的一组数据允许我们自定义声明。

3. Signature(签名)

用于验证消息的完整性和身份验证。通过将编码后的头部和负载与密钥结合,使用指定的算法生成签名。

有了 HEADER 和 PAYLOAD 两部分内容后,就可以让令牌携带信息了,似乎就可以在网络中传输了,但是在网络中传输这样的信息体是不安全的,因为你在“裸奔”啊。所以,我们还需要对其进行加密签名处理,而 SIGNATURE 就是对信息的签名结果,当受保护资源接收到第三方软件的签名后需要验证令牌的签名是否合法。

示例(使用 HMAC SHA256)

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  your-256-bit-secret
)

一个完整的 JWT 可能看起来像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

你可能会说,这个 JWT 令牌看起来也是毫无意义的、随机的字符串啊。确实,你直接去看这个字符串是没啥意义,但如果你把它拷贝到https://jwt.io/ 网站的在线校验工具中,就可以看到解码之后的数据:

JWT 验证

一般将 JWT 放在 HTTP 请求的 Authorization 头中,这种方式通常用于 API 身份验证。

验证 JWT(JSON Web Token)通常涉及以下几个步骤:解析 JWT、验证签名、检查有效载荷中的声明(如过期时间)

一般解析 JWT 通过第三方库来实现,PHP 可以使用 firebase/php-jwt 库

composer require firebase/php-jwt

https://github.com/firebase/php-jwt

PHP 示例

<?php
require 'vendor/autoload.php'; // 引入 Composer 自动加载文件
use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$key = 'mysecretkey';
$payload = [
    'iss' => 'http://example.org',
    'aud' => 'http://example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000,
    'user_id' => 123213,
    'user_name' => 'ryan',
];

/**
 * IMPORTANT:
 * You must specify supported algorithms for your application. See
 * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
 * for a list of spec-compliant algorithms.
 */
$jwt = JWT::encode($payload, $key, 'HS256');
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));

// Pass a stdClass in as the third parameter to get the decoded header values
$decoded = JWT::decode($jwt, new Key($key, 'HS256'), $headers = new stdClass());
print_r($headers);

/*
 NOTE: This will now be an object instead of an associative array. To get
 an associative array, you will need to cast it as such:
*/

$decoded_array = (array) $decoded;

/**
 * You can add a leeway to account for when there is a clock skew times between
 * the signing and verifying servers. It is recommended that this leeway should
 * not be bigger than a few minutes.
 *
 * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
 */
JWT::$leeway = 60; // $leeway in seconds
$decoded = JWT::decode($jwt, new Key($key, 'HS256'));

JWT 的使用场景

  1. 身份验证
    • 用户登录后,服务器生成 JWT 并返回给客户端。客户端在后续请求中将 JWT 作为身份验证凭证发送给服务器。
  2. 信息交换
    • JWT 可以安全地传递信息,因为它是经过签名的,接收方可以验证信息的完整性。

JWT 的优点

  1. 紧凑性:JWT 的结构小,适合在 URL、HTTP 头部或 Cookie 中传输。
  2. 自包含:JWT 包含所有必要的信息,避免多次查询数据库。
  3. 跨域支持:JWT 可以在不同的域之间传递,适合微服务架构。

JWT 的缺点

  1. 不可撤销:一旦 JWT 被签发,除非设置过期时间,否则无法撤销。
  2. 过期管理:需要合理管理 JWT 的过期时间,避免安全风险。
  3. 存储安全:如果 JWT 存储在客户端(如 Local Storage),可能会受到 XSS 攻击。

安全问题

  • 使用 HTTPS:确保 JWT 在传输过程中不被窃取。
  • 设置合理的过期时间:避免 JWT 长时间有效,降低被滥用的风险。
  • 使用强密钥:生成签名时使用强随机密钥,防止被破解。