一、前置准备
1. 安装库
-
使用 Composer 安装 firebase/php-jwt 是使用该库的前提。
composer require firebase/php-jwt
2. 核心类与方法
- 核心类:Firebase\JWT\JWT(所有操作围绕此类展开)
- 核心静态方法 :
- JWT::encode():生成 JWT 令牌
- JWT::decode():验证并解析 JWT 令牌
- 异常类:如 Firebase\JWT\ExpiredException、Firebase\JWT\SignatureInvalidException,用于捕获验证过程中的异常
二、核心参数说明
1. JWT::encode() 方法(生成 JWT)
方法签名:
public static function encode(
array $payload,
string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate $key,
string $alg = 'HS256',
string $keyId = null,
array $head = null
): string
参数详解:
|----------|-----------|----|------------------------------------------------------------------|
| 参数名 | 类型 | 必选 | 说明 |
| payload | 数组 | 是 | JWT 的载荷,存储业务数据(如用户ID、过期时间),建议包含注册声明 |
| key | 字符串/加密资源等 | 是 | 加密密钥: 1. 对称加密(HS256):自定义字符串密钥 2. 非对称加密(RS256):使用私钥(.pem文件内容) |
| alg | 字符串 | 否 | 加密算法,默认HS256 。常用: - 对称:HS256、HS384、HS512 - 非对称:RS256、RS384、RS512 |
| keyId | 字符串 | 否 | 密钥 ID,多密钥场景下标识当前密钥,写入 JWT 头部(kid字段) |
| $head | 数组 | 否 | 自定义 JWT 头部信息,合并默认头部(含alg,typ) |
常用注册声明(推荐在 $payload 使用):
- iss(issuer):签发者(如网站域名)
- aud(audience):接收者(如 API 域名)
- sub(subject):主题(如用户唯一标识)
- exp(expiration time):过期时间(Unix 时间戳,必填)
- nbf(not before):生效时间(Unix 时间戳)
- iat(issued at):签发时间(Unix 时间戳)
- jti(JWT ID):令牌唯一标识(防重放)
2. JWT::decode() 方法(验证并解析 JWT)
方法签名:
public static function decode(
string $jwt,
string|resource|OpenSSLAsymmetricKey|OpenSSLCertificate|JWKSet $key,
array $allowedAlgs = ['HS256']
): stdClass
参数详解:
|--------------|-----------|----|--------------------------------------------------|
| 参数名 | 类型 | 必选 | 说明 |
| jwt | 字符串 | 是 | 待验证的 JWT 令牌(三段式字符串,用.分隔) |
| key | 字符串/加密资源等 | 是 | 验证密钥: 1. 对称加密:与生成时相同的字符串密钥 2. 非对称加密:公钥(.pem文件内容) |
| $allowedAlgs | 数组 | 否 | 允许的加密算法列表,默认['HS256'],需与生成时一致,防止算法伪造攻击 |
返回值: 验证通过返回 stdClass 对象,包含 $payload 所有数据(可通过 -> 访问属性)。
三、实际使用示例
示例 1:对称加密(HS256,简单常用)
适合单体应用、前后端分离(密钥仅服务端保管)场景。
主要流程: 生成 JWT → 验证 JWT → 捕获异常
php
<?php
// 1. 引入 Composer 自动加载
require_once __DIR__ . '/vendor/autoload.php';
// 2. 导入核心类和异常类
use Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use UnexpectedValueException;
// 3. 配置参数
const SECRET_KEY = 'your-very-strong-secret-key-321-keep-it-safe';
const ALGORITHM = 'HS256';
// 生成 JWT 令牌
function generateJwtToken(int $userId): string
{
$payload = [
'iss' => 'https://example.com',
'aud' => 'https://api.example.com',
'sub' => (string)$userId,
'iat' => time(),
'exp' => time() + 3600,
'jti' => uniqid('jwt_', true),
'username' => 'test_user',
'role' => 'admin'
];
return JWT::encode($payload, SECRET_KEY, ALGORITHM);
}
// 验证并解析 JWT 令牌
function verifyJwtToken(string $jwt): ?stdClass
{
try {
$decoded = JWT::decode($jwt, SECRET_KEY, [ALGORITHM]);
return $decoded;
} catch (ExpiredException $e) {
echo "错误:JWT 令牌已过期 - " . $e->getMessage() . PHP_EOL;
} catch (SignatureInvalidException $e) {
echo "错误:JWT 签名无效 - " . $e->getMessage() . PHP_EOL;
} catch (UnexpectedValueException $e) {
echo "错误:JWT 格式无效 - " . $e->getMessage() . PHP_EOL;
} catch (Exception $e) {
echo "错误:JWT 验证失败 - " . $e->getMessage() . PHP_EOL;
}
return null;
}
// 测试
$jwtToken = generateJwtToken(10086);
echo "生成的 JWT 令牌:" . PHP_EOL . $jwtToken . PHP_EOL . PHP_EOL;
$decodedData = verifyJwtToken($jwtToken);
if ($decodedData) {
echo "JWT 验证通过,解析的数据:" . PHP_EOL;
echo "用户ID:" . $decodedData->sub . PHP_EOL;
echo "用户名:" . $decodedData->username . PHP_EOL;
echo "过期时间:" . date('Y-m-d H:i:s', $decodedData->exp) . PHP_EOL;
}
示例 2:非对称加密(RS256,适合分布式系统)
适合多服务协作(如微服务),私钥签发,公钥验签,安全性更高。
步骤 1:生成 RSA 密钥对(终端命令)
php
# 生成私钥(2048 位,无密码)
openssl genrsa -out private.pem 2048
# 从私钥生成公钥
openssl rsa -in private.pem -pubout -out public.pem
步骤 2:PHP 代码实现
php
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use UnexpectedValueException;
const PRIVATE_KEY_PATH = __DIR__ . '/private.pem';
const PUBLIC_KEY_PATH = __DIR__ . '/public.pem';
const ALGORITHM = 'RS256';
$privateKey = file_get_contents(PRIVATE_KEY_PATH);
$publicKey = file_get_contents(PUBLIC_KEY_PATH);
// 私钥生成 JWT
function generateRsaJwtToken(int $userId, string $privateKey): string
{
$payload = [
'iss' => 'https://example.com',
'sub' => (string)$userId,
'iat' => time(),
'exp' => time() + 3600,
'username' => 'test_user_rsa'
];
return JWT::encode($payload, $privateKey, ALGORITHM);
}
// 公钥验证 JWT
function verifyRsaJwtToken(string $jwt, string $publicKey): ?stdClass
{
try {
$decoded = JWT::decode($jwt, $publicKey, [ALGORITHM]);
return $decoded;
} catch (ExpiredException $e) {
echo "错误:JWT 令牌已过期 - " . $e->getMessage() . PHP_EOL;
} catch (SignatureInvalidException $e) {
echo "错误:JWT 签名无效 - " . $e->getMessage() . PHP_EOL;
} catch (UnexpectedValueException $e) {
echo "错误:JWT 格式无效 - " . $e->getMessage() . PHP_EOL;
} catch (Exception $e) {
echo "错误:JWT 验证失败 - " . $e->getMessage() . PHP_EOL;
}
return null;
}
// 测试
$jwtToken = generateRsaJwtToken(10087, $privateKey);
echo "生成的 RSA JWT 令牌:" . PHP_EOL . $jwtToken . PHP_EOL . PHP_EOL;
$decodedData = verifyRsaJwtToken($jwtToken, $publicKey);
if ($decodedData) {
echo "RSA JWT 验证通过,解析的数据:" . PHP_EOL;
echo "用户ID:" . $decodedData->sub . PHP_EOL;
echo "用户名:" . $decodedData->username . PHP_EOL;
}
四、总结汇总
核心要点回顾
- 核心操作:JWT::encode() 生成令牌,JWT::decode() 验证解析令牌,依赖密钥及算法。
- 参数核心:
- 生成:payload 必须含 exp,key 按算法类型选择(对称=字符串,非对称=私钥)
- 验证:key 与生成匹配(对称=相同字符串,非对称=公钥),allowedAlgs 包含生成算法
- 安全性注意:
- 不在 $payload 存储敏感信息(JWT 可被解码)
- 严格保管对称密钥/私钥,公钥可公开
- 捕获验证异常,区分异常类型做针对性处理
- 常用场景:
- 对称加密(HS256):单体应用、前后端分离身份认证
- 非对称加密(RS256):微服务、跨系统协作
常见问题提醒
- ExpiredException:检查 exp 是否为 Unix 时间戳且大于当前时间,注意服务器时区
- SignatureInvalidException:检查密钥匹配、算法一致、令牌未被篡改
- 令牌过长:$payload 仅存必要业务标识
总结
- firebase/php-jwt 以 JWT::encode()(生成)和 JWT::decode()(验证)为核心,安全性依赖密钥和算法。
- 对称加密(HS256)简单高效,适合单服务;非对称加密(RS256)安全性高,适合分布式系统。
- 生成 JWT 必须设置 exp 过期时间,验证时需捕获各类异常,且避免载荷存储敏感信息。