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

Last Updated:
贡献者: Tinywan