调试PHP验证Firebase JWT时遇算法密钥错误问题求助
问题分析与解决
核心问题
- 算法不匹配:你的JWT令牌头部显示使用的是
RS256算法(从eyJhbGciOiJSUzI1NiIs...可解码出alg: "RS256"),但代码里硬编码用了HS256——两者完全不兼容:RS256是非对称加密(私钥签名、公钥验证),HS256是对称加密(同一密钥完成签名+验证)。 - 公钥使用错误:从Google获取的公钥数组是
kid => 公钥内容的结构,你现在取的是$k[0](即kid字符串),而非对应的公钥内容,直接导致验证时密钥无效。
修正后的代码
public static function checkLogin(){ $headers = apache_request_headers(); // 从请求头中获取Bearer令牌,注意去掉多余引号 $jwt = null; if (isset($headers['Authorization'])){ $jwt = explode(' ', trim($headers['Authorization'], '"'))[1]; } else if (isset($headers['authorization'])) { $jwt = explode(' ', trim($headers['authorization'], '"'))[1]; } if (!$jwt) { die(json_encode(array("error" => true, "status" => 401, "message" => "NO TOKEN PROVIDED."))); } // 获取Google的Firebase签名公钥列表 $publicKeys = file_get_contents('https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com'); $publicKeys = json_decode($publicKeys, true); // 解析JWT头部,提取kid以匹配对应公钥 list($headerEncoded) = explode('.', $jwt); // 修复Base64URL编码的兼容问题 $header = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $headerEncoded)), true); $kid = $header['kid'] ?? null; if (!$kid || !isset($publicKeys[$kid])) { die(json_encode(array("error" => true, "status" => 401, "message" => "INVALID PUBLIC KEY."))); } JWT::$leeway = 60; try { // 使用RS256算法和匹配到的公钥验证令牌 $decoded = JWT::decode($jwt, new Key($publicKeys[$kid], 'RS256')); } catch (Exception $e) { die(json_encode(array("error" => true, "status" => 401, "message" => "TOKEN VERIFICATION FAILED: " . $e->getMessage()))); } $decoded_array = (array) $decoded; Utilities::debug($decoded_array); // JWT::decode已自动验证exp等字段,无需重复判断 return $decoded_array['exp']; }
关键说明
- 算法修正:Firebase的ID Token固定使用RS256算法签名,必须用对应公钥验证,不能用HS256。
- 公钥动态匹配:Google会定期轮换签名密钥,必须通过token中的
kid字段匹配当前有效的公钥,不能固定取第一个。 - 自动验证逻辑:
JWT::decode会自动校验令牌的过期时间(exp)、签发时间(iat)等核心字段,验证失败直接抛出异常,无需手动判断。 - 请求头处理:示例请求头中Authorization的值带双引号,需用
trim去除,否则解析出的令牌会包含无效字符导致验证失败。
内容的提问来源于stack exchange,提问作者digitalway
相关产品推荐
相关产品推荐

