Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Web Pushを(ちょっと)詳しく解説する #fukuokapwa

Web Pushを(ちょっと)詳しく解説する #fukuokapwa

福岡PWA勉強会 #01 - connpass
https://fukuoka-pwa.connpass.com/event/81842/

Hiroyuki ANAI

March 29, 2018
Tweet

More Decks by Hiroyuki ANAI

Other Decks in Programming

Transcript

  1. !QJSPTJLJDL
    ෱Ԭ18"ษڧձ

    View Slide

  2. w ݀Ҫ޺޾
    w IUUQTUXJUUFSDPNQJSPTJLJDL
    w ϠϑʔגࣜձࣾɹΤϯδχΞ
    w ϦονϥϘגࣜձࣾɹΤϯδχΞ
    w Ϡϑʔͷࢠձࣾ
    w +BWB4DSJQUͱ͔͕޷͖Ͱ͢
    !2

    View Slide

  3. 13
    w 3FBDUೖ໳ͱ͍͏ຊΛ

    ॻ͖·ͨ͠
    w 3FEVYʹ͍ͭͯ

    ͪΌΜͱॻ͍͍ͯΔຊ͸

    ͓ͦΒ͘͜Ε͚ͩ
    w Α͔ͬͨΒങ͍ͬͯͩ͘͞


    View Slide

  4. 13
    w 8%#13&44Ͱ

    ʮͲΜͱ͍͜ϑϩϯτΤϯυ
    ։ൃʯͱ͍͏࿈ࡌΛ΍͍ͬͯ
    ·͢
    w Α͔ͬͨΒಡΜͰ͍ͩ͘͞ʂ


    View Slide

  5. ΑΖ͓͘͠ئ͍͠·͢
    !

    View Slide

  6. 8FC1VTIΛ
    ʢͪΐͬͱʣৄ͘͠આ໌͢Δ

    View Slide

  7. ࿩͢͜ͱ
    w 8%#13&44WPMͰ

    18"ʹؔ͢ΔهࣄΛॻ͍ͨ
    w จྔͷؔ܎Ͱɺ

    ୲౰ͨ͠8FC1VTIͷ෦෼Λ

    ͔ͳΓ࡟ͬͨ
    w ΋͏ͪΐͬͱ͚ͩৄ͘͠

    ղઆ͍ͤͯͩ͘͞͞


    View Slide

  8. 8FC1VTI
    w 8FCͰϓογϡ͕ग़དྷΔ
    w ϒϥ΢βଆͷ"1*͸8$͕ɺ

    ϓογϡ͢Δଆͷϓϩτίϧ͸*&5'͕ඪ४Խ͍ͯ͠Δ
    w 18"$IFDLMJTUʹ͸ϓογϡ௨஌ͷ߲໨͕͋Γɺ

    8FC1VTI΋18"ͷҰ෦ͱݴͬͯ΋໰୊ͳ͍ʂʂʂʂ

    ʢ18"ͬΆ͘ͳ͍ൃදͰେมਃ͠༁ͳ͍"ʣ
    !8

    View Slide

  9. ϒϥ΢βଆͷ"1*

    View Slide

  10. w TDSJQU
    w ߪಡͷ։࢝
    w TFSWJDF8PSLFS3FHJTUSBUJPOTVCTDSJCF \ʜ^

    w 4FSWJDF8PSLFS
    w ϓογϡͷड৴
    w TFMGBEE&WFOU-JTUFOFS QVTI ʜ

    !10

    View Slide

  11. 1 // でロード・実行されるJavaScript<br/>2<br/>3 // サポート判定<br/>4 if (!('serviceWorker' in navigator && 'PushManager' in window)) {<br/>5 console.log('Web push is not supported in this browser');<br/>6 return;<br/>7 }<br/>8<br/>9 // ServiceWorkerの登録<br/>10 // ServiceWorkerRegistrationを返す<br/>11 const reg<br/>12 = await navigator.serviceWorker.register('./sw.js');<br/>13<br/>14 // 購読開始<br/>15 // ユーザに許可を求めるダイアログが表示され、<br/>16 // 許可されればPushSubscriptionを返す<br/>17 const pushSubscription = await reg.pushManager.subscribe({<br/>18 // プッシュサーバーの公開鍵<br/>19 applicationServerKey,<br/>20<br/>21 // "返されたプッシュサブスクリプションの効果が<br/>22 // ユーザーに表示するメッセージにだけ使われるかを示す"<br/>23 // https://mzl.la/2GgFKbs<br/>24 userVisibleOnly: true<br/>25 });<br/>!11<br/>

    View Slide

  12. 1 // PushSubscription
    2
    3 // プッシュサービスのエンドポイントURL
    4 pushSubscription.endpoint;
    5
    6 // クライアントで生成した鍵情報
    7 pushSubscription.getKey('auth');
    8 pushSubscription.getKey('p256dh');
    9
    10 // JSON.stringifyでシリアライズ可能
    11 console.log(JSON.stringify(pushSubscription));
    12 // => {"endpoint":"…",{"keys":{…}}}
    13
    14 // プッシュを送信するサーバーにPushSubscriptionを送る
    15 fetch('/send-to-push-server', {
    16 method: 'POST',
    17 headers: {
    18 'Content-Type': 'application/json'
    19 },
    20 body: JSON.stringify(pushSubscription)
    21 });
    !12

    View Slide

  13. 1 // ServiceWorker側のコード
    2
    3 // pushイベント: プッシュを受信した時に発火
    4 self.addEventListener('push', event => {
    5 // プッシュされたデータの取得
    6 event.data.text();
    7 event.data.json();
    8
    9 // 通知の表示
    10 return event.waitUntil(
    11 self.registration.showNotification(
    12 'タイトル',
    13 {
    14 body: '本文',
    15 icon: 'アイコン画像URL',
    16 …
    17 }
    18 )
    19 );
    20 });
    !13

    View Slide

  14. ϓογϡΛૹ৴͢Δ
    ॲཧ

    View Slide

  15. w XFCQVTIMJCTʹϥΠϒϥϦ͕͋Δ
    w IUUQTHJUIVCDPNXFCQVTIMJCT
    w ࠓճ͸/PEFKT൛Ͱ࣮૷
    w IUUQTHJUIVCDPNXFCQVTIMJCTXFCQVTI
    w OQNJOTUBMMTBWFXFCQVTI

    03

    ZBSOBEEXFCQVTI
    !15

    View Slide

  16. 1 // Node.js
    2 const webpush = require('web-push');
    3
    4 // 鍵を生成(最初の一度だけでよい)
    5 const vapidKeys = webpush.generateVAPIDKeys();
    6
    7 // base64url形式の文字列
    8 // 公開鍵
    9 // ブラウザ側のsubscribe関数呼出し時に渡す(applicationServerKey)
    10 vapidKeys.publicKey;
    11 // 秘密鍵
    12 vapidKeys.privatekey;
    13
    14 // 設定
    15 webpush.setVapidDetails(
    16 // subject
    17 // プッシュサービスが送信者に連絡を取る時に使う
    18 // URLかメールアドレス
    19 "mailto:[email protected]",
    20 // 生成した鍵ペア
    21 vapidKeys.publicKey,
    22 vapidKeys.privateKey
    23 );
    !16

    View Slide

  17. 1 // 送信するデータ
    2 const message = { … }
    3
    4 // メッセージを送信
    5 webpush.sendNotification(
    6 // ブラウザから受け取ったPushSubscription
    7 pushSubscription,
    8 // 文字列
    9 JSON.stringify(message)
    10 );
    !17

    View Slide

  18. σϞ

    View Slide

  19. ΋͏ͪΐͬͱৄ͘͠

    View Slide

  20. ͬ͘͟Γͱͨ͠શମ૾
    1VTI4FSWJDF 4FSWFS
    TVCTDSJCF
    1VTI4VCTDSJQUJPO
    1VTI4VCTDSJQUJPO
    4FOENFTTBHF
    4FOENFTTBHF
    "QQ
    !20

    View Slide

  21. αʔόʔͷೝূ
    1VTI4FSWJDF
    ਖ਼͍͠4FSWFS
    ѱ͍4FSWFS
    "QQ
    4FOENFTTBHF
    4FOENFTTBHF
    0,#
    /($
    !21

    View Slide

  22. αʔόʔͷೝূ
    1VTI4FSWJDF ਖ਼͍͠4FSWFS
    ॺ໊Λ෇༩
    ॺ໊Ͱ
    ਖ਼͍͠4FSWFS͔Βͷ
    ϝοηʔδͰ͋Δ͜ͱΛ֬ೝ
    "QQ
    0,#
    !22

    View Slide

  23. αʔόʔͷೝূ
    1VTI4FSWJDF ਖ਼͍͠4FSWFS
    ॺ໊Λ෇༩
    ॺ໊Ͱ
    ਖ਼͍͠4FSWFS͔Βͷ
    ϝοηʔδͰ͋Δ͜ͱΛ֬ೝ
    "QQ
    0,#
    7"1*%
    !23

    View Slide

  24. 7"1*%
    w IUUQTUPPMTJFUGPSHIUNMSGD
    w 7PMVOUBSZ"QQMJDBUJPO4FSWFS*EFOUJpDBUJPO

    GPS8FC1VTI
    w 8FC1VTIͷαʔόೝূ7"1*%Λࢼͯ͠ΈΔ چ୊($.
    ͷొ࿥͕ෆཁʹͳͬͨ$ISPNFͷ8FC1VTIΛࢼͯ͠ΈΔ

    2JJUB
    !24

    View Slide

  25. 1VTI4FSWJDF 4FSWFS
    "QQ
    ެ։伴
    ൿີ伴
    ᶃ伴ϖΞΛੜ੒
    ᶄࣄલʹެ։伴Λڞ༗
    ᶅTVCTDSJCF࣌ʹ
    ϓογϡαʔϏεʹެ։伴Λૹ৴
    ᶆൿີ伴Ͱॺ໊Λ࡞੒
    +85
    +85
    ᶇຊจʴॺ໊ɺެ։伴Λૹ৴
    ᶈೝূ͢Δ
    !25

    View Slide

  26. ᶃ伴ϖΞΛੜ੒
    !26

    View Slide

  27. 1 // webpush.generateVAPIDKeysの中身
    2 function generateVAPIDKeys() {
    3 // crptoモジュールでECDHの鍵ペアを生成
    4 const curve = crypto.createECDH('prime256v1');
    5 curve.generateKeys();
    6
    7 let publicKeyBuffer = curve.getPublicKey();
    8 let privateKeyBuffer = curve.getPrivateKey();
    9
    10 // Bufferをbase64url形式に変換する処理など
    11 …
    12 }
    13
    !27

    View Slide

  28. ᶄࣄલʹެ։伴Λڞ༗
    ᶅTVCTDSJCF࣌ʹ
    ɹϓογϡαʔϏεʹެ։伴Λૹ৴
    !28

    View Slide

  29. 1 // web-push/src/vapid-helper.js
    2
    3 function getVapidHeaders(audience, subject, publicKey, privateKey, …) {
    4 // …省略…
    5
    6 const header = {
    7 typ: 'JWT',
    8 alg: 'ES256'
    9 };
    10
    11 const jwtPayload = {
    12 aud: audience,
    13 exp: expiration,
    14 sub: subject
    15 };
    16
    17 // 署名:JWT(JSON Web Token)を生成
    18 const jwt = jws.sign({
    19 header: header,
    20 payload: jwtPayload,
    21 // 生成した秘密鍵で署名
    22 privateKey: toPEM(privateKey)
    23 });
    24
    ᶆൿີ伴Ͱॺ໊Λ࡞੒
    !29

    View Slide

  30. 1 // web-push/src/vapid-helper.js
    2
    3 function getVapidHeaders(audience, subject, publicKey, privateKey, …) {
    4 // …省略…
    5
    6 if (/* Content-Encodingがaes128gcmの場合 */) {
    7 return {
    8 Authorization: 'vapid t=' + jwt + ', k=' +
    urlBase64.encode(publicKey)
    9 };
    10 } else if (/* Content-Encodingがaes128の場合 */) {
    11 return {
    12 Authorization: 'WebPush ' + jwt,
    13 'Crypto-Key': 'p256ecdsa=' + urlBase64.encode(publicKey)
    14 };
    15 }
    16
    17 throw new Error(…);
    18 }
    19
    ᶇຊจʴॺ໊ɺެ։伴Λૹ৴
    ˞ࡉ͔͍ॲཧ͸ஔ͍͓͍ͯͯɺ
    ɹIFBEFSʹॺ໊ɺެ։伴ΛؚΊͯૹ৴͢Δͱ͍͏͜ͱ͕ݴ͍͔ͨͬͨ
    !30

    View Slide

  31. ϖΠϩʔυͷ҉߸Խ
    1VTI4FSWJDF ਖ਼͍͠4FSWFS
    "QQ
    ҉߸Խ



    ෮߸
    ҉߸Խ͞Ε͍ͯΔͷͰɺ
    1VTI4FSWJDF͸த਎ΛݟΕͳ͍
    !31

    View Slide

  32. ͳͥ҉߸Խ͕ඞཁ͔ʁ
    w 8FC1VTI͸ΞϓϦέʔγϣϯ͕ϓογϡαʔϏεΛ

    ҙࣝ͠ͳ͍ͰΑ͍ઃܭʹͳ͍ͬͯΔ
    w ϓογϡαʔϏεͷ"1*͕ಉ͡ͳͷͰ
    w ൓໘ɺ΋͔ͨ͠͠Β৴པͰ͖ͳ͍ϓογϡαʔϏεʹ

    σʔλΛૹ৴͍ͯ͠Δ͔΋͠Εͳ͍
    w ΞϓϦͷΫϥΠΞϯτɾαʔόҎ֎͸

    ϝοηʔδ͕ಡΊͳ͍Α͏ʹ͢΂͖
    !32

    View Slide

  33. ҉߸Խʹؔ͢Δ࢓༷
    w .FTTBHF&ODSZQUJPOGPS8FC1VTI

    IUUQTUPPMTJFUGPSHIUNMSGD
    w 3'$ˣΛ࢖ͬͯ҉߸Խ͢ΔखॱͳͲ
    w &ODSZQUFE$POUFOU&ODPEJOHGPS)551

    IUUQTUPPMTJFUGPSHIUNMSGD
    w )551্ͷϖΠϩʔυͷ҉߸Խɺ伴ަ׵ͳͲ
    !33

    View Slide

  34. ΋ͬͱৄ͘͠
    w 5IF8FC1VTI1SPUPDPMc(PPHMF%FWFMPQFST

    IUUQTEFWFMPQFSTHPPHMFDPNXFCGVOEBNFOUBMT
    QVTIOPUJpDBUJPOTXFCQVTIQSPUPDPM
    w ӳޠ͕ͩίʔυ෇͖ͰΠϝʔδ͠΍͍͢
    w 8FC1VTIͰϒϥ΢βʹϓογϡ௨஌ΛૹͬͯΈ
    Δ2JJUB
    w ࢓༷ͷߋ৽ʹ΋௥ै͓ͯ͠Γɺ௒ྑهࣄ
    !34

    View Slide

  35. ͓ΘΓʹ

    View Slide

  36. ϓϩτίϧʹ͍ͭͯ
    w ϓϩτίϧ͕γϯϓϧˍϥΠϒϥϦ͕͋ΔͷͰɺ

    1BB4ͳͲʹཔΒͣͱ΋ࣗલͰͳΜͱ͔ͳΔ
    w ·ͩ࢓༷͕มΘΔՄೳੑ͕͋Γͦ͏ͳͷͰɺ

    ࢓༷΍ϒϥ΢βʔͷ࣮૷ঢ়گΛ௥͍ͳ͕Βʹ͸ͳΔ
    !36

    View Slide

  37. ࢓༷ΛͪΌΜͱௐ΂ͯΈͯ
    w ϓϩτίϧ͕γϯϓϧͳͷͰɺ

    ؤுΕ͹શ෦ͷ࢓༷ॻΛಡΊΔ

    ʢ࣮ࡍʹ̍िؒҐͰ͘Β͍͸໨Λ௨ͤͨʣ
    w ࢓༷ॻΛಡΉͱʮಡΜͩͧʯͱ͍͏ؾ࣋ͪʹͳΓɺ

    ʢ໾ʹཱ͔ͭͱݴΘΕΔͱඍົ͕ͩʣṖͷຬ଍ײ͕͋Δ
    w ͕ɺ҉߸Խɾ伴पΓ͸ௐ΂࢝ΊΔͱ

    ͲΜͲΜΘ͔Βͳ͍͜ͱ͕ग़ͯ͘ΔͷͰ

    ·ͱ·͕ͬͨ࣌ؒඞཁ͔΋͠Εͳ͍
    !37

    View Slide


  38. View Slide