使用jwt-php及Firebase令牌生成器的HS256令牌签名无效问题
invalid_custom_token与签名验证失败问题 我之前也碰到过几乎一模一样的Firebase自定义令牌坑,结合你说的「修改UID或displayName任意字符就能验证通过」这个细节,大概率是字段隐形字符/编码问题或者签名密钥处理错误导致的,下面给你一步步排查和解决的思路:
1. 先排查UID和displayName的字符问题
你提到改一个字符就能过,这十有八九是这两个字段里藏了不可见的控制字符(比如全角空格、换行符、零宽空格),或者编码不是标准UTF-8。这些字符肉眼看不到,但会让JWT的payload哈希值变化,导致签名不匹配。
建议生成令牌前强制清洗字段:
// 去除首尾空白(包括隐形控制字符) $cleanUid = trim($yourOriginalUid); $cleanDisplayName = trim($yourOriginalDisplayName); // 强制转成UTF-8编码 if (mb_detect_encoding($cleanUid) !== 'UTF-8') { $cleanUid = mb_convert_encoding($cleanUid, 'UTF-8'); } if (mb_detect_encoding($cleanDisplayName) !== 'UTF-8') { $cleanDisplayName = mb_convert_encoding($cleanDisplayName, 'UTF-8'); }
用清洗后的字段重新生成令牌,先去jwt.io验证,大概率能解决签名问题。
2. 检查签名密钥的正确性
firebase-token-generator-php依赖服务账户的私钥,很多人复制密钥时会不小心带多余字符(比如JSON里的换行、空格,或者复制时多了首尾引号),导致签名算法无法正确计算哈希。
可以试试用jwt-php手动生成令牌对比:
use Firebase\JWT\JWT; // 确保私钥是完整读取的,不要手动复制粘贴 $privateKey = file_get_contents('path/to/your-service-account-private-key.pem'); $payload = [ 'iss' => 'your-service-account-email@your-project.iam.gserviceaccount.com', 'sub' => 'your-service-account-email@your-project.iam.gserviceaccount.com', 'aud' => 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit', 'uid' => $cleanUid, 'displayName' => $cleanDisplayName, 'iat' => time(), 'exp' => time() + 3600, // 有效期设1小时 'admin' => false ]; $token = JWT::encode($payload, $privateKey, 'RS256');
如果这个手动生成的令牌能在jwt.io验证通过,说明原来的firebase-token-generator-php库可能存在字段编码处理的bug(毕竟这个库已经好几年没更新了)。
3. 替换成官方推荐的生成方式
firebase-token-generator-php属于非官方维护的旧库,和最新版本的jwt-php兼容性很差。建议直接使用Firebase Admin SDK for PHP来生成自定义令牌,它会自动处理所有编码、签名细节,避免手动踩坑:
use Firebase\Admin\Auth\Auth; use Firebase\Admin\FirebaseAdmin; // 初始化Admin SDK $firebase = FirebaseAdmin::initialize([ 'credentials' => 'path/to/service-account.json' ]); $auth = $firebase->getAuth(); // 生成自定义令牌 $customToken = $auth->createCustomToken($cleanUid, ['displayName' => $cleanDisplayName]);
用官方SDK生成的令牌基本不会出现签名问题,而且后续维护也有保障。
4. 最后确认Payload的必填字段
确保你的JWT Payload里这几个字段完全正确:
iss和sub必须是你的服务账户邮箱aud必须是固定值:https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkitexp(过期时间)不能超过当前时间+3600秒(1小时)
内容的提问来源于stack exchange,提问作者11teenth

