PHP 中的 OAuth 2.0 和 JWT 简介
\n\n现代 Web 应用程序很少能脱离授权系统。两种最流行的协议——OAuth 2.0 和 JWT(JSON Web Token)——使开发人员能够创建安全且可扩展的解决方案。在本文中,我们将详细讲解如何从零开始在 PHP 中实现 OAuth 2.0 和 JWT 授权。本材料面向初学者,因此所有概念都将用通俗易懂的语言配合实际示例进行解释。
\n\nOAuth 2.0 是一种委托访问协议。它允许用户向第三方授予对其资源的有限访问权限,而无需传递密码。而 JWT 是一种紧凑的令牌格式,其中包含有关用户及其权限的所有必要信息。
\n\n什么是 JWT 以及它是如何工作的?
\n\nJWT(JSON Web Token)是一种开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,以 JSON 对象的形式在各方之间传输信息。令牌由三部分组成,用点分隔:
\n\n- \n
- Header(头部)——包含有关令牌类型和加密算法的信息(通常为 HS256 或 RS256)。 \n
- Payload(载荷)——包含“声明”(claims):用户数据、签发时间(iat)、过期时间(exp)等。 \n
- Signature(签名)——基于头部、载荷和密钥创建的签名。保证数据的完整性。 \n
典型的 JWT 如下所示:
\n\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ItCQ0L3QsNGC0L7Qu9C40LkiLCJpYXQiOjE1MTYyMzkwMjJ9.4vGJ7qF6b0Q9l8z3X5p2Rk1h7t9y2w4z6x8c0v2b4n6m8\n\nJWT 的优势在于服务器无需在数据库中存储会话——所有信息都已编码在令牌中。这使得系统易于扩展。
\n\n安装用于在 PHP 中处理 JWT 的库
\n\n为了在 PHP 中处理 JWT,我们将使用流行的库 firebase/php-jwt。通过 Composer 安装它:
composer require firebase/php-jwt\n\n创建和验证 JWT 令牌
\n\n让我们看一个基本示例:用户登录时创建令牌,并在请求受保护资源时验证令牌。
\n\n1. 生成令牌(login.php):
\n\n<?php\nrequire_once 'vendor/autoload.php';\n\nuse Firebase\\JWT\\JWT;\nuse Firebase\\JWT\\Key;\n\n$secretKey = '您的密钥-长度不少于32个字符';\n\n// 成功身份验证后的用户数据\n$payload = [\n 'iss' => 'pythonlib.ru', // 令牌签发者\n 'sub' => $userId, // 用户 ID\n 'name' => $userName, // 用户名\n 'role' => 'user', // 角色\n 'iat' => time(), // 签发时间\n 'exp' => time() + 3600 // 令牌1小时后过期\n];\n\n$jwt = JWT::encode($payload, $secretKey, 'HS256');\n\n// 将令牌发送给客户端\necho json_encode(['token' => $jwt]);\n?>\n\n2. 验证令牌(profile.php):
\n\n<?php\nrequire_once 'vendor/autoload.php';\n\nuse Firebase\\JWT\\JWT;\nuse Firebase\\JWT\\Key;\n\n$secretKey = '您的密钥-长度不少于32个字符';\n\n// 从 Authorization 头部获取令牌\n$headers = getallheaders();\n$authHeader = $headers['Authorization'] ?? '';\n\nif (preg_match('/Bearer\\s(\\S+)/', $authHeader, $matches)) {\n $jwt = $matches[1];\n \n try {\n $decoded = JWT::decode($jwt, new Key($secretKey, 'HS256'));\n \n // 令牌有效,获取用户数据\n $userId = $decoded->sub;\n $userName = $decoded->name;\n \n // 返回受保护的数据\n echo json_encode(['status' => 'success', 'user' => $decoded]);\n \n } catch (Exception $e) {\n // 令牌无效或已过期\n htt