JSON Web令牌(JWT)已成为Web开发中各方之间安全传输信息的流行方法。在本指南中,将探索在PHP中创建、验证和解码JWT令牌,而不依赖于外部库。我们将利用 hash_hmac
生成签名,利用 base64_encode/decode
进行编码和解码。
环境配置
在深入研究代码之前,请确保您的服务器上安装了PHP。此外,您可能希望使用Composer来管理依赖项。在这个例子中,我们将创建一个独立的类来处理JWT操作。
composer init
composer require guzzlehttp/guzzle
JWT 类
现在,让我们创建一个名为 Jwt
的类来封装JWT操作:
<?php
class Jwt
{
/**string */
private string $secretKey;
/**
* @param string $secretKey
* @author Tinywan(ShaoBo Wan)
*/
public function __construct(string $secretKey)
{
$this->secretKey = $secretKey;
}
/**
* @desc create token
* @param array $payload
* @return string
* @author Tinywan(ShaoBo Wan)
*/
public function createToken(array $payload): string
{
// Implementation for creating JWT
}
/**
* @desc validate token
* @param string $token
* @return bool
* @author Tinywan(ShaoBo Wan)
*/
public function validateToken(string $token): bool
{
// Implementation for validating JWT
}
/**
* @desc decode token
* @param string $token
* @return array
* @author Tinywan(ShaoBo Wan)
*/
public function decodeToken(string $token): array
{
// Implementation for decoding JWT
}
// Helper functions for base64 URL encoding/decoding
}
创建JWT
让我们实现用于生成JWT的 createToken
方法:
/**
* @desc create token
* @param array $payload
* @return string
* @author Tinywan(ShaoBo Wan)
*/
public function createToken(array $payload): string
{
$base64UrlHeader = $this->base64UrlEncode(json_encode(["alg" => "HS256", "typ" => "JWT"]));
$base64UrlPayload = $this->base64UrlEncode(json_encode($payload));
$base64UrlSignature = hash_hmac('sha256', $base64UrlHeader . '.' . $base64UrlPayload, $this->secretKey, true);
$base64UrlSignature = $this->base64UrlEncode($base64UrlSignature);
return $base64UrlHeader . '.' . $base64UrlPayload . '.' . $base64UrlSignature;
}
/**
* @desc base64_encode 编码
* @param string $data
* @return string
* @author Tinywan(ShaoBo Wan)
*/
private function base64UrlEncode(string $data): string
{
$base64 = base64_encode($data);
$base64Url = strtr($base64, '+/', '-_');
return rtrim($base64Url, '=');
}
/**
* @desc base64_encode 解码
* @param string $data
* @return string
* @author Tinywan(ShaoBo Wan)
*/
private function base64UrlDecode(string $data): string
{
$base64 = strtr($data, '-_', '+/');
$base64Padded = str_pad($base64, strlen($base64) % 4, '=', STR_PAD_RIGHT);
return base64_decode($base64Padded);
}
验证JWT
现在,让我们实现用于验证JWT的 validateToken
方法:
/**
* @desc validate token
* @param string $token
* @return bool
* @author Tinywan(ShaoBo Wan)
*/
public function validateToken(string $token): bool
{
list($base64UrlHeader, $base64UrlPayload, $base64UrlSignature) = explode('.', $token);
$signature = $this->base64UrlDecode($base64UrlSignature);
$expectedSignature = hash_hmac('sha256', $base64UrlHeader . '.' . $base64UrlPayload, $this->secretKey, true);
return hash_equals($signature, $expectedSignature);
}
解码JWT
最后,让我们实现用于解码JWT的 decodeToken
方法:
/**
* @desc decode token
* @param string $token
* @return array
* @author Tinywan(ShaoBo Wan)
*/
public function decodeToken(string $token): array
{
list(, $base64UrlPayload, ) = explode('.', $token);
$payload = $this->base64UrlDecode($base64UrlPayload);
return json_decode($payload, true);
}
验证
<?php
require 'vendor/autoload.php';
// Your secret key (keep this secure)
$secretKey = 'Tinywan2024040000011';
// Create an instance of Jwt
$jwt = new \Tinywan\Jwt($secretKey);
// Create a JWT
$payload = [
"user_id" => 2024,
"username" => "Tinywan",
"exp" => time() + 3600, // Token expiration time (1 hour)
];
$token = $jwt->createToken($payload);
echo 'JWT Token: ' . $token . PHP_EOL;
// Validate and decode the JWT
if ($jwt->validateToken($token)) {
echo 'JWT is valid.' . PHP_EOL;
$decodedPayload = $jwt->decodeToken($token);
echo "Decoded Payload: " . json_encode($decodedPayload, JSON_PRETTY_PRINT).PHP_EOL;
var_dump(json_decode(json_encode($decodedPayload), true));
} else {
echo 'JWT is invalid.' . PHP_EOL;
}
输出
JWT Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoyMDI0LCJ1c2VybmFtZSI6IlRpbnl3YW4iLCJleHAiOjE3MTI1ODcyNTN9.7agfeUJxj-iE0oevkf_lQwmYZOcCd7ORHWycDNv27_I
JWT is valid.
Decoded Payload: {
"user_id": 2024,
"username": "Tinywan",
"exp": 1712587253
}
array(3) {
["user_id"]=>
int(2024)
["username"]=>
string(7) "Tinywan"
["exp"]=>
int(1712587253)
}
在本例中,我们创建了一个JWT,其有效负载包含用户ID、用户名和过期时间。然后我们使用我们的 Jwt类验证和解码JWT。
源码地址:https://github.com/Tinywan/jwt