Русский
Русский
English
Статистика
Реклама

Push-notification

Браузерные Push-уведомления на Javascript и PHP

10.06.2021 02:22:28 | Автор: admin

Предисловие

В попытке найти годную статью по настройке уведомлений в браузере, я получал только статьи где в основном описывалось использование совместно с Firebase, но мне такой вариант не особенно то и подходил.

В данной статье не будут "размусолены" принципы работы и тонкости Push уведомлений, только код, только хардкор.

Важные замечания

Push-уведомления работают только с HTTPS.
К слову, в добавок с HTTPS должен присутствовать валидный SSL сертификат, подойдет и Let's Encrypt

Для разработки подойдёт localhost. Проблем возникнуть не должно, но если все же возникли данная статья поможет разобраться с ними.

Да будет код

Авторизация (VAPID)

Для начала стоит установить библиотеку WebPush в ваш php проект:

$ composer require minishlink/web-push

Далее для авторизации вашего сервера браузером (VAPID), вам нужно сгенерировать публичный и приватный ssh ключи. Данные ключи понадобятся как на сервере, так и на клиенте (за исключением того что на клиенте нужен лишь публичный).

Чтобы сгенерировать несжатый публичный и приватный ключ, закодированный в Base64, введите следующее в свой Linux bash:

$ openssl ecparam -genkey -name prime256v1 -out private_key.pem$ openssl ec -in private_key.pem -pubout -outform DER|tail -c 65|base64|tr -d '=' |tr '/+' '_-' >> public_key.txt$ openssl ec -in private_key.pem -outform DER|tail -c +8|head -c 32|base64|tr -d '=' |tr '/+' '_-' >> private_key.txt

Так же автор библиотеки предоставляет генерацию vapid ключей с помощью встроенного метода:

$vapidKeysInBase64 = VAPID::createVapidKeys();

Подписка

Этап 1 (JS)

В начале стоит проверить наличие поддержки ServiceWorker, PushManager, а так же showNotification в браузере:

app.js

function checkNotificationSupported() {return new Promise((fulfilled, reject) => {  if (!('serviceWorker' in navigator)) {      reject(new Error('Service workers are not supported by this browser'));      return;    }    if (!('PushManager' in window)) {      reject(new Error('Push notifications are not supported by this browser'));      return;    }    if (!('showNotification' in ServiceWorkerRegistration.prototype)) {      reject(new Error('Notifications are not supported by this browser'));    return;    }        fulfilled();  })}

Создаем файл sw.js и далее регистрируем его:

app.js

navigator.serviceWorker.register('sw.js').then(() => {      console.log('[SW] Service worker has been registered');    }, e => {      console.error('[SW] Service worker registration failed', e);    }  );

Так же нам понадобится функция для проверки состояния подписки:

app.js

function checkNotificationPermission() {    return new Promise((fulfilled, reject) => {        if (Notification.permission === 'denied') {            return reject(new Error('Push messages are blocked.'));        }        if (Notification.permission === 'granted') {            return fulfilled();        }        if (Notification.permission === 'default') {            return Notification.requestPermission().then(result => {                if (result !== 'granted') {                    reject(new Error('Bad permission result'));                } else {                    fulfilled();                }            });        }        return reject(new Error('Unknown permission'));    });}

С сервера нам нужно получить публичный ssh ключ сгенерированный выше:

<script>window.applicationServerKey = '<?= $yourPublicKeyFromServer ?>'</script>

Далее на ваше усмотрение, вешаем вызов окна на разрешение получение уведомлений. В моем примере человек через 10 секунд получает предложение подписаться.

app.js

document.addEventListener('DOMContentLoaded', documentLoadHandler);function documentLoadHandler() {    checkNotificationSupported()        .then(() => {          setTimeout(() => {            serviceWorkerRegistration.pushManager.subscribe({                    userVisibleOnly: true,                    applicationServerKey: urlBase64ToUint8Array(window.applicationServerKey),                })                .then(successSubscriptionHandler, errorSubscriptionHandler)          }, 10000);         },         console.error      );}function urlBase64ToUint8Array(base64String) {    const padding = '='.repeat((4 - (base64String.length % 4)) % 4);    const base64 = (base64String + padding).replace(/\-/g, '+').replace(/_/g, '/');    const rawData = window.atob(base64);    const outputArray = new Uint8Array(rawData.length);    for (let i = 0; i < rawData.length; ++i) {        outputArray[i] = rawData.charCodeAt(i);    }    return outputArray;}function errorSubscriptionHandler(err) {    if (Notification.permission === 'denied') {        console.warn('Notifications are denied by the user.');    } else {        console.error('Impossible to subscribe to push notifications', err);    }}

Далее если процесс получения разрешения подписки прошел успешно вызываем функцию successSubscriptionHandler

Формируем данные пользователя для дальнейшей отправки уведомлений.

app.js

function successSubscriptionHandler(subscriptionData) {    const key = subscription.getKey('p256dh');    const token = subscription.getKey('auth');    const contentEncoding = (PushManager.supportedContentEncodings || ['aesgcm'])[0];    const body = new FormData();    body.set('endpoint', subscription.endpoint);    body.set('publicKey', key ? btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null);    body.set('authToken', token ? btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null);    body.set('contentEncoding', contentEncoding);    return fetch('src/push_subscription.php', {      method,      body,    }).then(() => subscription);  }

Так же нам нужно сформировать отправляемое уведомление

Вы можете манипулировать данными уведомления при помощи Post Message API

self.addEventListener('push', function (event) {    if (!(self.Notification && self.Notification.permission === 'granted')) {        return;    }    const sendNotification = body => {        const title = "Заголовок уведомления";        return self.registration.showNotification(title, {            body,        });    };    if (event.data) {        const message = event.data.text();        event.waitUntil(sendNotification(message));    }});

Этап 2 (PHP)

Необходим php версии 7+

Далее в файле subscribeUserToPushNotifications на который мы сделали запрос с фронта при получении разрешения на подписку, мы формируем данные пользователя

subscribeUserToPushNotifications.php

<?php $subscription = $_POST;if (!isset($subscription['endpoint'])) {    echo 'Error: not a subscription';    return;}// save subscription from => $subscription 

На данном этапе мы можем записать данные пользователя в Базу данных (Ну или что у вас там), для последующей отправки уведомлений.

Непосредственно сама отправка происходит следующим образом

Достаем юзера с места его сохранения, и далее создаем объект подписчика:

pushNotificationToClient.php

<?php use Minishlink\WebPush\WebPush;use Minishlink\WebPush\Subscription;$subscription = Subscription::create($subscriptionData);

Далее формируем VAPID для авторизации:

pushNotificationToClient.php

<?php $auth = array(    'VAPID' => array(        'subject' => 'https://your-project-domain.com',        'publicKey' => file_get_contents(__DIR__ . '/your_project/keys/public_key.txt'),        'privateKey' => file_get_contents(__DIR__ . '/your_project/keys/private_key.txt'),     ));

После того как сформировали нужные данные, создаем новый объект WebPush:

pushNotificationToClient.php

<?php$webPush = new WebPush($auth);

Ура! Наконец мы можем отправить запрос на отправку Push уведомления

<?php$report = $webPush->sendOneNotification(  $subscription,  "Тело пуш уведомления, оно поступило в тело sw.js");

Важное замечание

Для отправки уведомлений в итерации, стоит использовать функцию, с теми же параметрами, что и в функции выше:

$webPush->queueNotification

Полезные источники

  1. О технологии push

  2. О WebPush от хабровчанина

  3. Библиотека WebPush

  4. Пример использования от разработчика библиотеки

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru