PHP原生JWT深度解析:从算法原理到安全实践

2352次阅读 172人点赞 作者: WuBin 发布时间: 2025-06-26 14:05:56
扫码到手机查看

什么是JWT?

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各方之间安全地传输信息作为JSON对象。它由三部分组成:

  1. 头部(Header)- 包含令牌类型和使用的哈希算法
  2. 有效载荷(Payload)- 包含声明(用户信息和其他数据)
  3. 签名(Signature)- 用于验证消息在传输过程中未被更改

准备工作

在开始之前,确保您的PHP环境满足以下要求:

  • PHP 7.2或更高版本
  • 启用的OpenSSL扩展(用于签名和验证)
  • 基本的PHP和HTTP协议知识

第一步:创建JWT类

首先,我们创建一个处理JWT生成和验证的类:

class JWT {
    private $secretKey;
    
    publicfunction __construct($secretKey) {
        $this->secretKey = $secretKey;
    }
    
    // 生成JWT
    publicfunction encode(array $payload, $expiry = 3600): string {
        $header = json_encode([
            'typ' => 'JWT',
            'alg' => 'HS256'
        ]);
        
        $now = time();
        $payload['iat'] = $now;
        $payload['exp'] = $now + $expiry;
        $payload = json_encode($payload);
        
        $base64UrlHeader = $this->base64UrlEncode($header);
        $base64UrlPayload = $this->base64UrlEncode($payload);
        
        $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $this->secretKey, true);
        $base64UrlSignature = $this->base64UrlEncode($signature);
        
        return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
    }
    
    // 验证并解码JWT
    publicfunction decode(string $token): ?array {
        $parts = explode('.', $token);
        
        if (count($parts) !== 3) {
            returnnull;
        }
        
        list($base64UrlHeader, $base64UrlPayload, $base64UrlSignature) = $parts;
        
        $signature = $this->base64UrlDecode($base64UrlSignature);
        $expectedSignature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $this->secretKey, true);
        
        if (!hash_equals($signature, $expectedSignature)) {
            returnnull;
        }
        
        $payload = json_decode($this->base64UrlDecode($base64UrlPayload), true);
        
        if (isset($payload['exp']) && $payload['exp'] < time()) {
            returnnull;
        }
        
        return $payload;
    }
    
    privatefunction base64UrlEncode(string $data): string {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
    
    privatefunction base64UrlDecode(string $data): string {
        return base64_decode(strtr($data, '-_', '+/'));
    }
}

第二步:用户认证和令牌发放

创建一个简单的用户认证系统来发放JWT:

// 模拟用户数据库
$users = [
    'user1' => password_hash('password1', PASSWORD_BCRYPT),
    'user2' => password_hash('password2', PASSWORD_BCRYPT)
];

// 初始化JWT类
$jwt = new JWT('your-secret-key-here');

// 处理登录请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'], $_POST['password'])) {
    $username = $_POST['username'];
    $password = $_POST['password'];
    
    if (isset($users[$username]) && password_verify($password, $users[$username])) {
        // 认证成功,生成JWT
        $token = $jwt->encode([
            'sub' => $username,
            'role' => 'user'
        ]);
        
        // 可以设置为HTTP-only cookie或返回给客户端
        setcookie('jwt', $token, time() + 3600, '/', '', false, true);
        echo json_encode(['token' => $token]);
        exit;
    } else {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid credentials']);
        exit;
    }
}

第三步:保护受限制的路由

创建一个中间件函数来验证JWT并保护路由:

function authenticate() {
    $jwt = new JWT('your-secret-key-here');
    
    // 从Cookie或Authorization头获取令牌
    $token = $_COOKIE['jwt'] ?? null;
    if (!$token && isset($_SERVER['HTTP_AUTHORIZATION'])) {
        if (preg_match('/Bearer\s(\S+)/', $_SERVER['HTTP_AUTHORIZATION'], $matches)) {
            $token = $matches[1];
        }
    }
    
    if (!$token) {
        http_response_code(401);
        echo json_encode(['error' => 'Token not provided']);
        exit;
    }
    
    $payload = $jwt->decode($token);
    if (!$payload) {
        http_response_code(401);
        echo json_encode(['error' => 'Invalid or expired token']);
        exit;
    }
    
    return $payload;
}

// 受保护的路由示例
$payload = authenticate();
echo"Welcome, " . htmlspecialchars($payload['sub']) . "!";

第四步:刷新令牌

实现令牌刷新机制以延长会话:

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_COOKIE['jwt'])) {
    $payload = $jwt->decode($_COOKIE['jwt']);
    
    if ($payload && isset($payload['sub'])) {
        // 验证令牌即将过期(例如在最后5分钟内)
        if ($payload['exp'] - time() < 300) {
            $newToken = $jwt->encode([
                'sub' => $payload['sub'],
                'role' => $payload['role']
            ]);
            
            setcookie('jwt', $newToken, time() + 3600, '/', '', false, true);
            echo json_encode(['token' => $newToken]);
            exit;
        }
    }
    
    http_response_code(400);
    echo json_encode(['error' => 'Token cannot be refreshed']);
    exit;
}

安全注意事项

  1. 密钥安全:确保您的JWT密钥足够复杂并安全存储
  2. HTTPS:始终通过HTTPS传输JWT以防止中间人攻击
  3. 令牌过期:设置合理的过期时间以减少被盗用的风险
  4. 敏感数据:不要在JWT中存储敏感信息,因为它可以被解码(但不是加密的)
  5. 注销处理:实现令牌黑名单或使用短期令牌来处理注销

相关资料

点赞 支持一下 觉得不错?客官您就稍微鼓励一下吧!
关键词:jwt,jsonwebtoken
推荐阅读
  • python基础-操作列表和迭代器

    python基础笔记-操作列表和迭代器的相关方法

    6381次阅读 142人点赞 发布时间: 2024-06-13 13:26:27 立即查看
  • uniapp实现被浏览器唤起的功能

    当用户打开h5链接时候,点击打开app若用户在已经安装过app的情况下直接打开app,若未安装过跳到应用市场下载安装这个功能在实现上主要分为两种场景,从普通浏览器唤醒以及从微信唤醒。

    11062次阅读 744人点赞 发布时间: 2022-12-14 16:34:53 立即查看
  • PHP

    【正则】一些常用的正则表达式总结

    在日常开发中,正则表达式是非常有用的,正则表达式在每个语言中都是可以使用的,他就跟JSON一样,是通用的。了解一些常用的正则表达式,能大大提高你的工作效率。

    14946次阅读 608人点赞 发布时间: 2021-10-09 15:58:58 立即查看
  • 【中文】免费可商用字体下载与考证

    65款免费、可商用、无任何限制中文字体打包下载,这些字体都是经过长期验证,经得住市场考验的,让您规避被无良厂商起诉的风险。

    14068次阅读 1129人点赞 发布时间: 2021-07-05 15:28:45 立即查看
  • Vue

    Vue3开发一个v-loading的自定义指令

    在vue3中实现一个自定义的指令,有助于我们简化开发,简化复用,通过一个指令的调用即可实现一些可高度复用的交互。

    17944次阅读 1438人点赞 发布时间: 2021-07-02 15:58:35 立即查看
  • JS

    关于手机上滚动穿透问题的解决

    当页面出现浮层的时候,滑动浮层的内容,正常情况下预期应该是浮层下边的内容不会滚动;然而事实并非如此。在PC上使用css即可解决,但是在手机端,情况就变的比较复杂,就需要禁止触摸事件才可以。

    16144次阅读 1304人点赞 发布时间: 2021-05-31 09:25:50 立即查看
  • Vue

    Vue+html2canvas截图空白的问题

    在使用vue做信网单页专题时,有海报生成的功能,这里推荐2个插件:一个是html2canvas,构造好DOM然后转canvas进行截图;另外使用vue-canvas-poster(这个截止到2021年3月...

    31675次阅读 2491人点赞 发布时间: 2021-03-02 09:04:51 立即查看
  • Vue

    vue-router4过度动画无效解决方案

    在初次使用vue3+vue-router4时候,先后遇到了过度动画transition进入和退出分别无效的情况,搜遍百度没没找到合适解决方法,包括vue-route4有一些API都进行了变化,以前的一些操...

    27786次阅读 2146人点赞 发布时间: 2021-02-23 13:37:20 立即查看
交流 收藏 目录