构建push package - Safari Push Notifications

1. 说明

Safari官方文档比较简单,生成 push package 的方法 createPushPackage.php 也没有更新…

官方文档

2. 优化后的 createPushPackage 的方法
class CreatePushPackage {
    static $cert_path = '你的 web push 证书.p12';
    static $cert_password = '证书密码';
    static $intermediate_cert_path = '中间证书.pem' // 获取中间证书方法:见文末
    static $origin_dir = '存放原始配置文件目录路径';
    static $package_dir = '存放配置文件目录路径';
    static $version = 'v2';
    static $zip_path = '';

    public static function raw_files() {
        return array(
            'icon.iconset/icon_16x16.png',
            'icon.iconset/icon_16x16@2x.png',
            'icon.iconset/icon_32x32.png',
            'icon.iconset/icon_32x32@2x.png',
            'icon.iconset/icon_128x128.png',
            'icon.iconset/icon_128x128@2x.png',
            'website.json'
        );
    }

    public static function copy_raw_push_package_files() {
        if (!is_dir(self::$package_dir . '/icon.iconset')) {
            mkdir(self::$package_dir . '/icon.iconset');
        }
        foreach (self::raw_files() as $raw_file) {
            copy(self::$origin_dir . $raw_file, self::$package_dir . '/' .$raw_file);
            if(strpos($raw_file, "website.json") !== false) {
                $wjson = file_get_contents(self::$package_dir . '/' .$raw_file);
                $wjson = str_replace("{HTTP_HOST}", HTTP_HOST, $wjson);
                unlink(self::$package_dir . '/' .$raw_file);
                $ff = fopen(self::$package_dir . '/' .$raw_file, "x");
                fwrite($ff, $wjson);
                fclose($ff);
            }
        }
    }

    // Creates the manifest by calculating the SHA1 hashes for all of the raw files in the package.
    public static function create_manifest() {
        // Obtain SHA1 hashes of all the files in the push package
        $manifest_data = array();
        foreach (self::raw_files() as $raw_file) {
            $fileContent = file_get_contents(self::$package_dir . '/' .$raw_file);
            if (self::$version == 'v1') {
                $manifest_data[$raw_file] = sha1($fileContent);
            } else if (self::$version == 'v2') {
                $manifest_data[$raw_file]['hashType'] = 'sha512';
                $manifest_data[$raw_file]['hashValue'] = hash('sha512', $fileContent);
            }
        }
        file_put_contents(self::$package_dir . '/manifest.json', json_encode((object)$manifest_data));
    }

    // Creates a signature of the manifest using the push notification certificate.
    public static function create_signature() {
        // Load the push notification certificate
        $pkcs12 = file_get_contents(self::$cert_path);

        $certs = array();
        if(!openssl_pkcs12_read($pkcs12, $certs, self::$cert_password)) {
            return;
        }

        $signature_path = self::$package_dir . '/signature';

        // Sign the manifest.json file with the private key from the certificate
        $cert_data = openssl_x509_read($certs['cert']);
        $private_key = openssl_pkey_get_private($certs['pkey'], self::$cert_password);
        openssl_pkcs7_sign(self::$package_dir . '/manifest.json', $signature_path, $cert_data, $private_key, array(), PKCS7_BINARY | PKCS7_DETACHED, self::$intermediate_cert_path);

        // Convert the signature from PEM to DER
        $signature_pem = file_get_contents($signature_path);
        $matches = array();
        if (!preg_match('~Content-Disposition:[^\n]+\s*?([A-Za-z0-9+=/\r\n]+)\s*?-----~', $signature_pem, $matches)) {
            return;
        }
        $signature_der = base64_decode($matches[1]);
        file_put_contents($signature_path, $signature_der);
    }

    // Zips the directory structure into a push package, and returns the path to the archive.
    public static function package_raw_data() {
        if (file_exists(self::$zip_path)) {
            unlink(self::$zip_path);
        }

        // Package files as a zip file
        $zip = new \ZipArchive();
        if (!$zip->open(self::$zip_path, \ZIPARCHIVE::CREATE)) {
            Logger::Log('Could not create push package zip: ' . self::$zip_path, Logger::SCOPE_BROWSER);
            return;
        }

        $raw_files = self::raw_files();
        $raw_files[] = 'manifest.json';
        $raw_files[] = 'signature';
        foreach ($raw_files as $raw_file) {
            $zip->addFile(self::$package_dir . '/' . $raw_file, $raw_file);
        }

        $zip->close();
        return self::$zip_path;
    }

    // Creates the push package, and returns the path to the archive.
    public static function create_push_package() {        
        self::$zip_path = self::$package_dir . '_' . str_replace('.', '_', HTTP_HOST) . '_' . self::$version . '.zip';
        // if (file_exists(self::$zip_path)) {
        //     return self::$zip_path;
        // }

        if (!is_dir(self::$package_dir)) {
            mkdir(self::$package_dir);
        }

        self::copy_raw_push_package_files();
        self::create_manifest();
        self::create_signature();
        $package_path = self::package_raw_data();

        return $package_path;
    }
}
使用方法

获取到权限后,Safari浏览器会构造出下面地址
在这里插入图片描述

// 如果有log 会回调到 v1 方法
public function v1()
{
    $type = trim($this->uri->segment(3));
    if ($type == 'log') {
    	// 记录相关log
        Log('get push packages error >> ' . file_get_contents('php://input'));
        return true;
    }

    return true;
}

// 请求 push package
public function v2()
{
    $type = trim($this->uri->segment(3));
    $pushID = trim($this->uri->segment(4));

    if ($type == 'pushPackages' && $pushID == '你的 web push ID') {
        $package_path = CreatePushPackage::create_push_package();
        if (empty($package_path)) {
            http_response_code(500);
            die;
        }

        header("Content-type: application/zip");
        header("Content-Disposition: attachment; filename=pushpackage.zip");
        header("Content-length: " . filesize($package_path));
        header("Pragma: no-cache");
        header("Expires: 0");
        echo file_get_contents($package_path);
        die;
    }
    return true;
}

获取中间证书

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值