PHP 中的 JWT(JSON Web 令牌),无需使用第 3 方库。怎么签?

JWT (JSON Web Token) in PHP without using 3rd-party library. How to sign?

在 PHP 中有一些用于实现 JSON Web 令牌 (JWT) 的库,例如 php-jwt. I am writing my own, very small and simple class but cannot figure out why my signature fails validation here,尽管我已尝试遵守标准。我已经尝试了几个小时,但我被困住了。请帮忙!

我的代码很简单

//build the headers
$headers = ['alg'=>'HS256','typ'=>'JWT'];
$headers_encoded = base64url_encode(json_encode($headers));

//build the payload
$payload = ['sub'=>'1234567890','name'=>'John Doe', 'admin'=>true];
$payload_encoded = base64url_encode(json_encode($payload));

//build the signature
$key = 'secret';
$signature = hash_hmac('SHA256',"$headers_encoded.$payload_encoded",$key);

//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature";
echo $token;

base64url_encode函数:

function base64url_encode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

我的 headers 和负载与 validation site's 默认 JWT 完全匹配,但我的签名不匹配,因此我的令牌被标记为无效。这个标准看起来很简单,所以我的签名有什么问题?

我解决了!我没有意识到签名本身需要进行 base64 编码。此外,我需要将 hash_hmac 函数的最后一个可选参数设置为 $raw_output=true(参见 the docs。简而言之,我需要更改我的代码:

//build the signature
$key = 'secret';
$signature = hash_hmac('sha256',"$headers_encoded.$payload_encoded",$key);

//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature";

更正:

//build the signature
$key = 'secret';
$signature = hash_hmac('sha256',"$headers_encoded.$payload_encoded",$key,true);
$signature_encoded = base64url_encode($signature);

//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature_encoded";
echo $token;

如果你想使用 RS256(而不是像 OP 那样的 HS256)来解决它,你可以这样使用它:

//build the headers
$headers = ['alg'=>'RS256','typ'=>'JWT'];
$headers_encoded = base64url_encode(json_encode($headers));

//build the payload
$payload = ['sub'=>'1234567890','name'=>'John Doe', 'admin'=>true];
$payload_encoded = base64url_encode(json_encode($payload));

//build the signature
$key = "-----BEGIN PRIVATE KEY----- ....";
openssl_sign("$headers_encoded.$payload_encoded", $signature, $key, 'sha256WithRSAEncryption'); 
$signature_encoded = base64url_encode($signature);

//build and return the token
$token = "$headers_encoded.$payload_encoded.$signature_encoded";
echo $token;

我花了比我承认的更长的时间

https://github.com/gradus0/appleAuth 查看方法 $appleAuthObj->get_jwt_token()

<?php
include_once "appleAuth.class.php";

// https://developer.apple.com/account/resources/identifiers/list/serviceId -- indificator value
$clientId = ""; // com.youdomen
// your developer account id -> https://developer.apple.com/account/#/membership/
$teamId = "";
// key value show in -> https://developer.apple.com/account/resources/authkeys/list
$key = ""; 
// your page url where this script
$redirect_uri = ""; // example: youdomen.com/appleAuth.class.php
// path your key file, download file this -> https://developer.apple.com/account/resources/authkeys/list
$keyPath =''; // example: ./AuthKey_key.p8 

try{    
    $appleAuthObj = new \appleAuth\sign($clientId,$teamId,$key,$redirect_uri,$keyPath); 
    
    if(isset($_REQUEST['code'])){
        $jwt_token = $appleAuthObj->get_jwt_token($_REQUEST['code']);
        $response = $appleAuthObj->get_response($_REQUEST['code'],$jwt_token);
        $result_token = $this->read_id_token($response['read_id_token']);

        var_dump($response);
        var_dump($result_token);
    }else{
        $state = bin2hex(random_bytes(5));
        echo "<a href='".$appleAuthObj->get_url($state)."'>sign</a>";
    }
                                                                                    
} catch (\Exception $e) {
    echo "error: ".$e->getMessage();
}