【问题标题】:php push notification failingphp推送通知失败
【发布时间】:2020-01-29 07:34:14
【问题描述】:

我正在尝试向 pushSubscription 端点发送 JWT 推送通知。 当我回显结果时,我得到“提供的 JWT 无效”我不知道它为什么不起作用。我在 php 中生成 ECDSA 签名,返回的 JWT 在 jwt.io 中经过有效测试。 注意:密钥不在生产中,提供给上下文

/*GET ENDPOINT*/
$endpoint_push = json_decode($subscription)->endpoint;
$public_key_push = 'BNQCrj2wbXHBAK1hyjvc9R5zjypBwWG6szD_STnDPy2ORVUqTWZD304JS5LTHK5ywYS2w-aRouH3EjxLG9bWla8';

$token_push = '';




//PREPARE PUSH//

// Create token header as a JSON string
$header = json_encode(['typ' => 'JWT', 'alg' => 'ES256']);

// Create token payload as a JSON string
$payload = json_encode(['aud' => 'https://fcm.googleapis.com', 'exp' => '1516239022', 'sub' => 'mailto:push@example.com']);

// Encode Header to Base64Url String
$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));

// Encode Payload to Base64Url String
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));


// Create Signature Hash

$privateKeyString =
"-----BEGIN EC PARAMETERS-----
BgUrgQQACg==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHQCAQEEIHI6VMaMwvRag0foPp87+nhby3QrftcEsBHee6sdr0aZoAcGBSuBBAAK
oUQDQgAE91vCtp7tO4FyJbpgSS824PiuLR7LPNdwt+rcIe0uE19RUJz2Jgm8tRRD
HmBVzoQXNxcwVD1HfRMtU0wnUJOuAQ==
-----END EC PRIVATE KEY-----";

$privateKey = openssl_get_privatekey($privateKeyString);




$alg = OPENSSL_ALGO_SHA256;
$signature = null;

openssl_sign($base64UrlHeader . "." . $base64UrlPayload, $signature, $privateKey, $alg);




// Encode Signature to Base64Url String
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

// Create JWT
$token_push = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;







//SEND PUSH//
$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $endpoint_push);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);

$headers = array();
$headers[] = 'Ttl: 60';
$headers[] = 'Content-Length: 0';
$headers[] = 'Authorization: vapid t='.$token_push.', k='.$public_key_push.'';
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$result = curl_exec($ch);

curl_close($ch);




echo json_encode($result);

【问题讨论】:

    标签: php push-notification jwt


    【解决方案1】:

    所以我基本上尝试了完全相同的事情,但我花了一段时间才意识到 OpenSSL 将其大部分输出包装在 ASN.1 格式的结构中,而 JWT 令牌需要您先将其剥离。

    这是一个工作脚本,希望其他人会觉得有用。

    请注意,此脚本不会将任何数据附加到推送消息中,因此 Web 客户端将收到没有上下文的推送消息,并且他们无权访问(据我所知)JWT 对象要么,所以也不能放任何有用的数据。

    当然有一种方法可以将加密数据附加到请求中,这里不做介绍。

    <?php
    
    /*
     * If you get any part of this process wrong, Google gives the really helpful error message "invalid JWT provided".
     * 
     * Mozilla (Firefox) gives a slightly just-as-useful error:
     * {
     *   "code": 401, "errno": 109, "error": "Unauthorized",
     *   "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes",
     *   "message": "Request did not validate Invalid Authorization Header"
     * }
     */
    
    // Generate the keys like this, although you can probably do it in PHP.
    // `openssl ecparam -genkey -name prime256v1 -noout -out server-push-ecdh-p256.pem &>/dev/null`;
    // `openssl ec -in server-push-ecdh-p256.pem -pubout -out server-push-ecdh-p256.pub &>/dev/null`;
    
    
    $privk = file_get_contents('server-push-ecdh-p256.pem');
    $pubk = file_get_contents('server-push-ecdh-p256.pub');
    $endpoint = "https://fcm.googleapis.com/fcm/send/some-really-long-unique-secret-string-that-your-web-client-gets-on-push-subscribe";
    $contact = "mailto:your-admin-email-address@example.com";
    
    function base64web_encode($a) {
        return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($a));
    }
    function base64web_decode($a) {
        return base64_decode(str_replace(['-', '_', ''], ['+', '/', '='], $a));
    }
    
    $asn = new phpseclib\File\ASN1();
    
    $header = [
        "typ" => "JWT",
        "alg" => "ES256"
    ];
    $claims = [
        // just the https://hostname part
        "aud" => substr($endpoint, 0, strpos($endpoint, '/', 10)),
        // this push message will be discarded after 24 hours of non-delivery
        "exp" => time() + 86400,
        // who the server can talk to if our push script is causing problems
        "sub" => $contact
    ];
    
    /*
     * Note these need to be base64 url-safe encoded, not standard base64.
     * @see https://tools.ietf.org/html/rfc4648#section-5
     */
    $strHeader = base64web_encode(json_encode($header));
    $strPayload = base64web_encode(json_encode($claims));
    
    $toSign = $strHeader . '.' . $strPayload;
    
    $signature = '';
    if (!openssl_sign($toSign, $signature, $privk, OPENSSL_ALGO_SHA256)) {
        trigger_error('sign failed: '. openssl_error_string());
    }
    
    /*
     * openssl_sign produces a signature which is the hash wrapped in an 
     * ASN.1 structure, so we need to extract the 256-bit raw hash manually. 
     * There's no PHP function to do this, so we use a library.
     */
    $xx = $asn->decodeBER($signature);
    /** @var \phpseclib\Math\BigInteger $a */
    /** @var \phpseclib\Math\BigInteger $b */
    $a = $xx[0]['content'][0]['content']; // 128-bits
    $b = $xx[0]['content'][1]['content']; // 128-bits
    $signature = $a->toBytes() . $b->toBytes();
    $strSignature = base64web_encode($signature);
    
    /*
     * This is now a complete JWT object.
     */
    $jwt = $strHeader . '.' . $strPayload . '.' . $strSignature;
    
    /*
     * Our PEM formatted public key is wrapped in an ASN.1 structure, so just 
     * like our signature above, lets extract
     * the raw public key part, which is the bit we need.
     */
    $xx = $pubk;
    $xx = str_replace(['-----BEGIN PUBLIC KEY-----','-----END PUBLIC KEY-----',"\n"], '', $xx);
    $xx = base64_decode($xx);
    $xx = $asn->decodeBER($xx);
    $xx = $xx[0]['content'][1]['content'];
    $xx = substr($xx, 1); // need to strip the first char, which is not part of the key
    $xx = base64web_encode($xx);
    $pubkey = $xx;
    
    /*
     * We need to append the public key used for signing this JWT object, so 
     * the server can validate the JWT and compare the public key against the 
     * push-registration by the client, where we said which public key we would 
     * accept pushes from.
     */
    $headers = [
        "Authorization: vapid t=$jwt,k=$pubkey",
        "Content-length: 0",
        "Ttl: 86400",
    ];
    
    /**
     * Push!
     */
    $ch = curl_init($endpoint);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_exec($ch);
    $ct = curl_multi_getcontent($ch);
    echo curl_error($ch);
    curl_close($ch);
    echo $ct;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-18
      • 2014-04-04
      • 1970-01-01
      相关资源
      最近更新 更多