2017-02-21 29 views
9

我爲我的網站開發了推送通知服務。該服務人員是:Web Pushnotification'UnauthorizedRegistration'or'Gone'or'Unauthorized'- subscription expires

'use strict'; 
    self.addEventListener('push', function (event) {  
    var msg = {}; 
    if (event.data) { 
     msg = event.data.json(); 
    }  
    let notificationTitle = msg.title; 
    const notificationOptions = { 
     body: msg.body,//body 
     dir:'rtl',//direction 
     icon: msg.icon,//image 
     data: { 
      url: msg.url,//click 
     }, 
    }; 
    event.waitUntil(
     Promise.all([ 
     self.registration.showNotification(
      notificationTitle, notificationOptions),  
     ]) 
    ); 
    }); 
    self.addEventListener('notificationclick', function (event) {  
     event.notification.close(); 

    let clickResponsePromise = Promise.resolve(); 
    if (event.notification.data && event.notification.data.url) { 
     clickResponsePromise = clients.openWindow(event.notification.data.url); 
    } 
    const fetchOptions = 
     { method: 'post'}; 
    fetch('http://localhost:5333/usrh.ashx?click=true', fetchOptions). 
    then(function (response) 
    { 
     if (response.status >= 400 && response.status < 500) 
     {   
      throw new Error('Failed to send push message via web push protocol'); 
     } 
    }).catch((err) => 
    { 
     this.showErrorMessage('Ooops Unable to Send a Click', err); 
    }); 
}); 

self.addEventListener('notificationclose', function (event) { 
    const fetchOptions = 
     { method: 'post'}; 
    fetch('http://localhost:5333/usrh.ashx?close=true', fetchOptions). 
    then(function (response) 
    { 
     if (response.status >= 400 && response.status < 500) 
     {   
      throw new Error('Failed to send push message via web push protocol'); 
     } 
    }).catch((err) => 
    { 
     this.showErrorMessage('Ooops Unable to Send a Click', err); 
    }); 
}); 
self.addEventListener('pushsubscriptionchange', function() { 
    const fetchOptions = { 
     method: 'post' 
     , 
    }; 

    fetch('http://localhost:5333/usru.ashx', fetchOptions) 
     .then(function (response) { 
      if (response.status >= 400 && response.status < 500) { 
       console.log('Failed web push response: ', response, response.status); 
       throw new Error('Failed to update users.'); 
      } 
     }) 
     .catch((err) => { 
      this.showErrorMessage('Ooops Unable to Send a user', err); 
     }); 
}); 

我已經成功地使用下面的代碼訂閱用戶:

registerServiceWorker() { 
     if ('serviceWorker' in navigator) { 
      navigator.serviceWorker.register('http://localhost:5333/service-worker.js') 
       .catch((err) => { 
        this.showErrorMessage('Unable to Register SW', 'Sorry this demo requires a service worker to work and it ' + 'failed to install - sorry :('); 
        console.error(err); 
       }); 
     } else { 
      this.showErrorMessage('Service Worker Not Supported', 'Sorry this demo requires service worker support in your browser. ' + 
       'Please try this demo in Chrome or Firefox Nightly.'); 
     } 
    } 

class PushClient { 
    constructor(subscriptionUpdate, appkeys) { 
     this._subscriptionUpdate = subscriptionUpdate; 
     this._publicApplicationKey = appkeys; 
     if (!('serviceWorker' in navigator)) { 
      return; 
     } 
     if (!('PushManager' in window)) { 
      return; 
     } 
     if (!('showNotification' in ServiceWorkerRegistration.prototype)) { 
      return; 
     } 
     navigator.serviceWorker.ready.then(() => { 
      this.setUpPushPermission(); 
     }); 
    } 
    setUpPushPermission() { 
     return navigator.serviceWorker.ready.then((serviceWorkerRegistration) => { 
      return serviceWorkerRegistration.pushManager.getSubscription(); 
     }) 
      .then((subscription) => { 
       if (!subscription) { 
        return; 
       } 
       this._subscriptionUpdate(subscription); 
      }) 
      .catch((err) => { 
       console.log('setUpPushPermission() ', err); 
      }); 
    } 
    subscribeDevice() { 
     return new Promise((resolve, reject) => { 
      if (Notification.permission === 'denied') { 
       sc(3); 
       return reject(new Error('Push messages are blocked.')); 
      } 
      if (Notification.permission === 'granted') { 
       sc(3); 
       return resolve(); 
      } 
      if (Notification.permission === 'default') { 
       Notification.requestPermission((result) => { 
        if (result === 'denied') { 
         sc(0); 
        } else if (result === 'granted') { 
         sc(1); 
        } else { 
         sc(2); 
        } 
        if (result !== 'granted') { 
         reject(new Error('Bad permission result')); 
        } 
        resolve(); 
       }); 
      } 
     }) 
      .then(() => { 
       return navigator.serviceWorker.ready.then((serviceWorkerRegistration) => { 
        return serviceWorkerRegistration.pushManager.subscribe({ 
         userVisibleOnly: true 
         , applicationServerKey: this._publicApplicationKey.publicKey 
        , 
        }); 
       }) 
        .then((subscription) => { 
         this._subscriptionUpdate(subscription); 
         if (subscription) { 
          this.sendPushMessage(subscription); 
         } 
        }) 
        .catch((subscriptionErr) => { }); 
      }) 
      .catch(() => { }); 
    } 
    toBase64(arrayBuffer, start, end) { 
     start = start || 0; 
     end = end || arrayBuffer.byteLength; 
     const partialBuffer = new Uint8Array(arrayBuffer.slice(start, end)); 
     return btoa(String.fromCharCode.apply(null, partialBuffer)); 
    } 
    unsubscribeDevice() { 
     navigator.serviceWorker.ready.then((serviceWorkerRegistration) => { 
      return serviceWorkerRegistration.pushManager.getSubscription(); 
     }) 
      .then((pushSubscription) => { 
       if (!pushSubscription) { 
        this._subscriptionUpdate(null); 
        return; 
       } 
       return pushSubscription.unsubscribe() 
        .then(function (successful) { 
         if (!successful) { 
          console.error('We were unable to unregister from push'); 
         } 
        }); 
      }) 
      .then(() => { 
       this._subscriptionUpdate(null); 
      }) 
      .catch((err) => { 
       console.error('Error thrown while revoking push notifications. ' + 'Most likely because push was never registered', err); 
      }); 
    } 
    sendPushMessage(subscription) { 
     let payloadPromise = Promise.resolve(null); 
     payloadPromise = JSON.parse(JSON.stringify(subscription)); 
     const vapidPromise = EncryptionHelperFactory.createVapidAuthHeader(this._publicApplicationKey, subscription.endpoint, 'http://localhost:5333/'); 
     return Promise.all([payloadPromise, vapidPromise, ]) 
      .then((results) => { 
       const payload = results[0]; 
       const vapidHeaders = results[1]; 
       let infoFunction = this.getWebPushInfo; 
       infoFunction =() => { 
        return this.getWebPushInfo(subscription, payload, vapidHeaders); 
       }; 
       const requestInfo = infoFunction(); 
       this.sendRequestToProxyServer(requestInfo); 
      }); 
    } 
    getWebPushInfo(subscription, payload, vapidHeaders) { 
     let body = null; 
     const headers = {}; 
     headers.TTL = 60; 
     if (payload) { 
      headers.Encryption = `auth=${payload.keys.auth}`; 
      headers['Crypto-Key'] = `p256dh=${payload.keys.p256dh}`; 
      headers['Content-Encoding'] = 'aesgcm'; 
     } else { 
      headers['Content-Length'] = 0; 
     } 
     if (vapidHeaders) { 
      headers.Authorization = `WebPush ${vapidHeaders.authorization}`; 
      if (headers['Crypto-Key']) { 
       headers['Crypto-Key'] = `${headers['Crypto-Key']}; ` + `p256ecdsa=${vapidHeaders.p256ecdsa}`; 
      } else { 
       headers['Crypto-Key'] = `p256ecdsa=${vapidHeaders.p256ecdsa}`; 
      } 
     } 
     const response = { 
      headers: headers 
      , endpoint: subscription.endpoint 
     , 
     }; 
     if (body) { 
      response.body = body; 
     } 
     return response; 
    } 
    sendRequestToProxyServer(requestInfo) { 
     const fetchOptions = { 
      method: 'post' 
     , 
     }; 
     if (requestInfo.body && requestInfo.body instanceof ArrayBuffer) { 
      requestInfo.body = this.toBase64(requestInfo.body); 
      fetchOptions.body = requestInfo; 
     } 
     fetchOptions.body = JSON.stringify(requestInfo); 
     fetch('http://localhost:5333/usrh.ashx', fetchOptions) 
      .then(function (response) { 
       if (response.status >= 400 && response.status < 500) { 
        console.log('Failed web push response: ', response, response.status); 
        throw new Error('Failed to send push message via web push protocol'); 
       } 
      }) 
      .catch((err) => { 
       this.showErrorMessage('Ooops Unable to Send a Push', err); 
      }); 
    } 
} 

所有這些代碼是在JavaScript。我可以在我的服務器像成功收到用戶訂閱infromarion:

Authorization: WebPush eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwcxxxxx 
Crypto-Key: p256dh=BBp90dwDWxxxxc1TfdBjFPqxxxxxwjO9fCip-K_Eebmg=; p256ecdsa=BDd3_hVL9fZi9Yboxxxxxxo 
endpoint: https://fcm.googleapis.com/fcm/send/cxxxxxxxxxxxxxxJRorOMHKLQ3gtT7 
Encryption: auth=9PzQZ1mut99qxxxxxxxxxxyw== 
Content-Encoding: aesgcm 

我也可以成功使用的代碼發送推給這個用戶波紋管在C#:

public static async Task<bool> SendNotificationByte(string endpoint, string[] Keys, byte[] userSecret, byte[] data = null, 
             int ttl = 0, ushort padding = 0, bool randomisePadding = false, string auth="") 
     { 
      #region send 
      HttpRequestMessage Request = new HttpRequestMessage(HttpMethod.Post, endpoint);     
       Request.Headers.TryAddWithoutValidation("Authorization", auth); 
      Request.Headers.Add("TTL", ttl.ToString()); 
      if (data != null && Keys[1] != null && userSecret != null) 
      { 
       EncryptionResult Package = EncryptMessage(Decode(Keys[1]), userSecret, data, padding, randomisePadding); 
       Request.Content = new ByteArrayContent(Package.Payload); 
       Request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); 
       Request.Content.Headers.ContentLength = Package.Payload.Length; 
       Request.Content.Headers.ContentEncoding.Add("aesgcm"); 
       Request.Headers.Add("Crypto-Key", "dh=" + Encode(Package.PublicKey)+" ;"+Keys[2]+"="+Keys[3]); 
       Request.Headers.Add("Encryption", "salt=" + Encode(Package.Salt)); 
      } 
      using (HttpClient HC = new HttpClient()) 
      { 
       HttpResponseMessage res = await HC.SendAsync(Request).ConfigureAwait(false); 
       if (res.StatusCode == HttpStatusCode.Created) 
        return true; 
       else return false; 
      } 
      #endregion 
     } 

的問題是,一段時間後的時間(約20小時甚至更短),當我想一推送到該用戶我得到了以下錯誤:

火狐訂閱:

{StatusCode: 410, ReasonPhrase: 'Gone', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: 
{ 
    Access-Control-Allow-Headers: content-encoding,encryption,crypto-key,ttl,encryption-key,content-type,authorization 
    Access-Control-Allow-Methods: POST 
    Access-Control-Allow-Origin: * 
    Access-Control-Expose-Headers: location,www-authenticate 
    Connection: keep-alive 
    Cache-Control: max-age=86400 
    Date: Tue, 21 Feb 2017 08:19:03 GMT 
    Server: nginx 
    Content-Length: 179 
    Content-Type: application/json 
}} 

鉻訂閱:

{StatusCode: 400, ReasonPhrase: 'UnauthorizedRegistration', Version: 1.1, Content: System.Net.Http.StreamContent, Headers: 
{ 
    X-Content-Type-Options: nosniff 
    X-Frame-Options: SAMEORIGIN 
    X-XSS-Protection: 1; mode=block 
    Alt-Svc: quic=":443"; ma=2592000; v="35,34" 
    Vary: Accept-Encoding 
    Transfer-Encoding: chunked 
    Accept-Ranges: none 
    Cache-Control: max-age=0, private 
    Date: Tue, 21 Feb 2017 08:18:35 GMT 
    Server: GSE 
    Content-Type: text/html; charset=UTF-8 
    Expires: Tue, 21 Feb 2017 08:18:35 GMT 
}} 

我想我錯過了一些東西,這使得訂閱過期,或者必須做出時,他們的簽約信息變更或已過期的用戶重新訂閱,但我不知道該如何? !

回答

1

問題通過向預訂用戶發送推送回聲通知來重新訂閱它們來解決。我寫了一篇工作,我定期發送一個推送回聲,並重新訂閱用戶並更新他們的信息。

爲此我送所謂的「推回聲」使用代碼波紋管的特殊消息:

self.addEventListener('push', function (event) { 
lastEventName = 'push'; 
var msg = {}; 
if (event.data) { 
    msg = event.data.json(); 
    if (!!msg.isEcho) { 
     self.registration.pushManager.getSubscription() 
      .then(function (subscription) { 
       if (!subscription) { 
       } else { 
        subscription.unsubscribe().then(function() { 
         self.registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: base64UrlToUint8Array('xxxxxxxxxxxxxxxx') }) 
          .then(function (subscription) { 
           resubscription(subscription); 
          }); 
        }); 
        } 
      }); 
     return; 
    } 
} 
if (!!msg.isEcho) 
    return; 
let notificationTitle = msg.title; 
const notificationOptions = { 
    body: msg.body, 
    dir: 'rtl', 
    icon: msg.icon, 
    data: { 
     url: msg.url, 
     id: msg.id, 
     key: msg.key 
    }, 
}; 
event.waitUntil(
    Promise.all([ 
    self.registration.showNotification(
     notificationTitle, notificationOptions), 
    ]) 
); 

const fetchOptions = 
    { method: 'post', mode: 'no-cors' }; 
fetch('http://example.com', fetchOptions). 
    then(function (response) { 
     if (response.status >= 400 && response.status < 500) { 
      throw new Error('Failed to send push message via web push protocol'); 
     } 
     lastEventName = 'view'; 
    }).catch((err) => { 
     this.showErrorMessage('Ooops Unable to Send a Click', err); 
    }); 
}); 

resubscription方法,你可以unsubscribe然後subscribe用戶和更新服務器數據。

1

一些線索:

的SW需要註冊並激活,在推動事件到達的時間。這意味着您可能無法清理會話,使用隱私瀏覽模式,清理電腦之間的緩存。

推送事件必須來自同一起源。

1

我認爲這個問題是關於你如何發送你的applicationServerKey。我只是做了你想做的事的例子,我不得不送這個功能編碼的關鍵:

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; 
    } 

所以你必須用這種方式來創建你的認購對象:

registration.pushManager 
    .subscribe({ 
     userVisibleOnly: true, 
     applicationServerKey: urlBase64ToUint8Array(this._publicApplicationKey.publicKey), 
    }) 

主要是我按照this tutorial。 我在this github repo有這個工作示例。 README文件使用西班牙文,但我認爲它可以幫助你。

+0

我不這麼認爲!如果這是解決方案,我永遠不會發送推送通知。正如我提到的,我可以發送給用戶約20小時!之後,似乎訂閱已過期! – Alex

+0

在代碼被刪除之前,我已經編碼了我的密鑰。 – Alex

2

我認爲這個問題可以通過重新訂閱用戶來解決。