JWT (JSON Web Token) 是一种开放标准(RFC 7519),用于安全地在各方之间传输信息。JWT 是一种自包含的令牌,通常用于身份验证和信息交换。它可以有效地在客户端和服务器之间传输身份信息,而不需要服务器存储会话状态。
JWT 主要由三部分组成:
- Header(头部)
- Payload(负载)
- Signature(签名)
JWT 的结构
一个完整的 JWT 是由三个部分组成的,它们用 .
分隔,结构如下:
header.payload.signature
1. Header
Header 部分通常由两部分组成:
- alg(算法):签名所使用的算法(如 HMAC SHA256 或 RSA)。
- typ(类型):通常为
JWT
。
示例:
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
Payload 部分包含了要传输的数据(即声明)。JWT 可以包含三种类型的声明:
- 注册声明:如
sub
(主题)、exp
(过期时间)、iat
(签发时间)等。 - 公共声明:自定义的数据声明,应该避免冲突。
- 私有声明:用于双方之间共享的信息。
示例:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
3. Signature
签名用于验证消息是否未被篡改,并且用于验证消息的来源。为了创建签名,需要使用 Header 和 Payload 的内容,并用一个密钥(secret
)和指定的算法(如 HMAC SHA256)来生成。
签名的生成方法如下:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret)
JWT 的生成与验证流程
1. 创建 JWT
为了生成 JWT,你需要做以下几步:
- 创建 Header 和 Payload。
- 使用密钥生成签名。
- 将 Header、Payload 和 Signature 使用
.
连接起来。
示例代码(使用 JavaScript 生成 JWT):
// 创建 Header
const header = {
alg: "HS256",
typ: "JWT"
};
// 创建 Payload
const payload = {
sub: "1234567890",
name: "John Doe",
iat: Math.floor(Date.now() / 1000) // 当前时间戳
};
// 创建密钥
const secret = 'your-256-bit-secret';
// Base64Url 编码函数
function base64UrlEncode(input) {
return Buffer.from(input).toString('base64')
.replace(/=/g, '') // 去掉 padding(=)
.replace(/\+/g, '-') // 替换 +
.replace(/\//g, '_'); // 替换 /
}
// 编码 Header 和 Payload
const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
// 创建签名
const crypto = require('crypto');
const signature = crypto
.createHmac('sha256', secret)
.update(encodedHeader + '.' + encodedPayload)
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
// 最终生成 JWT
const jwt = `${encodedHeader}.${encodedPayload}.${signature}`;
console.log(jwt);
2. 验证 JWT
验证 JWT 主要包括以下几个步骤:
- 解码 Header 和 Payload。
- 使用相同的算法和密钥重新计算签名。
- 将计算出来的签名与 JWT 中的签名进行比较。
示例代码(使用 JavaScript 验证 JWT):
function verifyJWT(token, secret) {
// 分解 token
const [encodedHeader, encodedPayload, signature] = token.split('.');
// 重新计算签名
const recalculatedSignature = crypto
.createHmac('sha256', secret)
.update(encodedHeader + '.' + encodedPayload)
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
// 比较签名
return recalculatedSignature === signature;
}
const isValid = verifyJWT(jwt, 'your-256-bit-secret');
console.log(isValid ? "JWT is valid!" : "JWT is invalid!");
3. 使用 JWT 的常见场景
- 身份验证:当用户登录时,服务器创建 JWT 并将其发送到客户端,客户端将其保存在本地存储(如 localStorage 或 cookie)中。每次请求时,客户端将 JWT 包含在请求头中,服务器验证其有效性后提供相应的资源。
- 信息交换:由于 JWT 是自包含的,它不仅可以用于身份验证,还可以用于在系统间交换信息。
4. 使用 JWT 与服务器端
PHP 中使用 JWT
在 PHP 中,使用第三方库来生成和验证 JWT 是最常见的做法。例如,firebase/php-jwt
库是一个流行的选择。
安装 firebase/php-jwt
:
composer require firebase/php-jwt
生成 JWT:
<?php
use \Firebase\JWT\JWT;
$key = "your_secret_key";
// 构建 payload
$payload = array(
"iss" => "http://example.org", // 签发者
"aud" => "http://example.com", // 接收者
"iat" => time(), // 签发时间
"exp" => time() + 3600 // 过期时间
);
// 生成 JWT
$jwt = JWT::encode($payload, $key);
echo $jwt;
?>
验证 JWT:
<?php
use \Firebase\JWT\JWT;
$key = "your_secret_key";
$jwt = $_GET['jwt']; // 从请求中获取 JWT
try {
$decoded = JWT::decode($jwt, $key, array('HS256'));
print_r($decoded);
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage();
}
?>
5. JWT 的优缺点
优点:
- 无状态:JWT 是自包含的,不需要存储在服务器端,减少了服务器的负担。
- 可跨域:由于其结构化的特性,可以轻松地跨不同的域和服务传递和验证。
- 灵活性:JWT 适用于身份验证、信息交换、API 密钥等场景。
缺点:
- 不易撤销:JWT 不能被直接撤销,除非你在服务端维护一个“黑名单”。
- 过期问题:JWT 具有过期时间,可能需要处理过期和刷新 token 的问题。
- 安全问题:如果使用弱的密钥或不安全的算法,可能导致安全漏洞。
6. 总结
JWT 是一种强大的机制,广泛用于 Web 应用中的身份验证和信息交换。它的自包含特性使得它不需要服务器存储会话信息,并且可以灵活地跨域和跨平台使用。然而,开发时应考虑到安全性,使用强密钥和合适的加密算法来确保其安全性。
如果你有关于 JWT 的更详细问题或需要其他实现的例子,随时告诉我!
发表回复