JSON Web Token(JWT)的使用

  首先使用composer下载JWT组件
  [root@localhost ~]# composer require firebase/php-jwt



  下面是封装好的JWT工具类

<?php
declare(strict_types=1);
ini_set('display_errors', 'On');
error_reporting(-1);

require_once __DIR__ . '/JWT/vendor/autoload.php'; // 引入JWT库

// 使用多台服务器时,服务器之间的系统时间可能不同步,A服签发JWT后客户端立即发往B服验证,如果B服比A服慢了几秒,就会出现验证失
// 败的情况,而通过设置“JWT::$leeway”属性就能够解决这个问题,它能容许两台服务器的系统时间有指定范围内的差距,若设置为0则要
// 求所有服务器的系统时间完全同步。服务器之间的系统时间差异通常不会太大,一般设为60秒就可以满足实际需求。
Firebase\JWT\JWT::$leeway = 60; // 默认为0,单位:秒

/**
 * JSON Web Token(JWT)工具类
 */
class JWTTool
{
    private const ISS = 'www.manong.life'; // JWT的签发者
    private const KEY = 'www.manong.life'; // 加解密所需的密钥
    private const ALG = 'HS512'; // 加密使用的哈希算法,缺省则为'HS256'
    private const EXP = 7200; // JWT失效时间(注意不宜过长,一般为2~24小时),单位:秒

    /**
     * JWT编码(即生成JWT)
     *
     * @param array $data 自定义数据(支持各种基本数据类型)
     * @return string JWT
     */
    public static function encode(mixed $data): string
    {
        $time = time();

        // 构造JWT的载荷(载荷可为空数组,但建议至少设置生效时间和失效时间)
        $payload = [
            'iss' => self::ISS, // JWT的签发者
            'iat' => $time, // JWT生成时间
            'nbf' => $time, // JWT生效时间,通常生成即生效,若想该JWT在10分钟后才生效可设为($time + 600)
            'exp' => $time + self::EXP, // JWT失效时间
            'data' => $data, // JWT的自定义数据
        ];

        return Firebase\JWT\JWT::encode($payload, self::KEY, self::ALG);
    }

    /**
     * JWT解码
     *
     * @param string $jwt JWT
     * @return mixed 自定义数据(签发JWT时自定义数据是什么类型就返回什么类型),若JWT不合法或已过期则返回null
     */
    public static function decode(string $jwt): mixed
    {
        $data = null;

        $key = new Firebase\JWT\Key(self::KEY, self::ALG);

        $payload = null;
        try {
            // JWT解码方法返回的是对象格式的整个载荷,若JWT不合法或已过期期则会抛出异常
            $payload = Firebase\JWT\JWT::decode($jwt, $key);
        } catch (Exception $e) {
            unset($e);
        }

        // JWT解码成功(即获得载荷),开始从载荷里取出自定义数据
        if ($payload instanceof stdClass) {
            // 将对象格式的载荷转为JSON字符串格式
            try {
                $payload = json_encode($payload, JSON_THROW_ON_ERROR);
            } catch (JsonException $e) {
                unset($e);
            }

            // 将JSON字符串格式的载荷转为数组格式
            if (is_string($payload)) {
                try {
                    $payload = json_decode($payload, true, 512, JSON_THROW_ON_ERROR);
                } catch (JsonException $e) {
                    unset($e);
                }
            }

            // 获取载荷里的自定义数据(即data部分)
            if (is_array($payload) && isset($payload['data'])) {
                $data = $payload['data'];
            }
        }

        return $data;
    }
}

/**
 * 打印变量的相关信息
 *
 * @param mixed $value 要打印的表达式
 * @param mixed ...$values 更多要打印的表达式
 * @return void echo
 */
function v(mixed $value, mixed ...$values): void
{
    ob_start(); // 打开输出控制缓冲
    var_dump($value);
    echo ob_get_clean(); // 从缓冲区获取var_dump()的内容,然后清空缓冲区

    foreach ($values as $v) {
        v($v); // 递归
    }
}

// 生成(签发)JWT的自定义数据
$data = ['uid' => 9527, 'name' => '张三', 'gender' => '男', 'birth' => 2003];

// 生成JWT(返回给客户端,客户端需将JWT保存到本地)
$jwt = JWTTool::encode($data);
echo "JWT:$jwt" . PHP_EOL; // JWT:{$header}.{$payload}.{$signature}

// 解码JWT,若JWT不合法或已过期则返回null
$profile = JWTTool::decode($jwt);
v($profile);
// array(4) {
//   ["uid"]=>
//   int(9527)
//   ["name"]=>
//   string(6) "张三"
//   ["gender"]=>
//   string(3) "男"
//   ["birth"]=>
//   int(2003)
// }


//========== 总结 ==========//
// 1、JWT由头部(header)、载荷(payload)、签名(signature)三部分组成,每部分之间用小数点隔开。
// 2、开发者的自定义数据就保存在载荷里面,需要注意的是载荷只是进行了base64_encode()处理,并没有加密,所以开发者务必先自行把自定义数
//    据加密,建议使用openssl_encrypt()或openssl_public_encrypt()加密,然后再签发JWT。
// 3、不要在JWT里保存太多自定义数据,只保存一两个重要的数据即可,如用户UID、账号之类的数据(再次提醒,自定义数据务必加密)。
// 4、使用JWT保存登录态很简单,就是客户端向服务端提交账号密码并通过身份认证后,服务端不保存session,而是向客户端返回一个包含其身份
//    信息的Token(即JWT),客户端需将JWT保存到本地,后续客户端向服务器端发起请求都要传递JWT,服务端验证JWT合法后才响应其请求。
// 5、客户端向服务端传递JWT的方式有多种,包括HTTP头、URL、POST等,通常建议使用名为Authorization的HTTP头将JWT传递给服务端。
// 6、JWT一旦签发给客户端就无法撤销,只能等其到期失效,故失效时间不宜过长,一般为2~24小时。开发者可提供刷新JWT功能,方法也很简单,
//    就是从JWT中提取出自定义数据,再用这些数据签发新的JWT给客户端,客户端用新JWT替换掉旧JWT即可。
// 7、JWT可以签发给任意客户端,包括但不限于Web站点(cookie和localStorage)、Android、iOS、微信小程序等客户端,只要该客户端可以保存
//    JWT字符串即可。

Copyright © 2024 码农人生. All Rights Reserved