Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Web Pushを(ちょっと)詳しく解説する #fukuokapwa
Search
Hiroyuki ANAI
March 29, 2018
Programming
700
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Web Pushを(ちょっと)詳しく解説する #fukuokapwa
福岡PWA勉強会 #01 - connpass
https://fukuoka-pwa.connpass.com/event/81842/
Hiroyuki ANAI
March 29, 2018
More Decks by Hiroyuki ANAI
See All by Hiroyuki ANAI
安心して変更できるWebフロントエンドの作り方
pirosikick
7
3.4k
書き換えて学ぶTemporal #fukts
pirosikick
2
510
fukuoka.ts #3 社内でESLintの共通設定を配りたい2025年春版
pirosikick
3
510
compilerOptions、全部読んだ
pirosikick
1
320
Step Functionsの設計時に知っておいたほうがいいかもしれないこと
pirosikick
0
560
Go言語による並行処理「4.4 orチャネル」の図
pirosikick
0
510
サイボウズWebフロントエンド脱レガシーの今までとこれから
pirosikick
6
17k
@cybozu/eslint-configから学ぶ、全社共通ESLint configの運用
pirosikick
4
1.9k
Web Share Target API #w3fukuoka
pirosikick
0
750
Other Decks in Programming
See All in Programming
AI が書く Go コードの品質を劇的に向上させる Linter: “declscope”
mpyw
0
490
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.4k
JAWS-UG 東京支部が始める、JAWS-UG支部コラボ / JAWS-UG lunchtime LT Collaboration
y0hgi
0
250
プロダクトコードからライブラリの境界を見つける
elmetal
PRO
0
110
Androidだけじゃない、DroidKaigiで広がった私の技術スタック
akkeylab
0
130
気持ちぃ〜角丸 Squircle
ryu1013
0
290
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
34
16k
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
590
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
570
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
150
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
250
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
380
Featured
See All Featured
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
850
It's Worth the Effort
3n
188
29k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
The Language of Interfaces
destraynor
162
27k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
780
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
4
1.2k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
Stop Working from a Prison Cell
hatefulcrawdad
274
21k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
320
AI Search: Where Are We & What Can We Do About It?
aleyda
0
8.1k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
33
5.2k
Transcript
!QJSPTJLJDL Ԭ18"ษڧձ
w ݀Ҫ w IUUQTUXJUUFSDPNQJSPTJLJDL w ϠϑʔגࣜձࣾɹΤϯδχΞ w ϦονϥϘגࣜձࣾɹΤϯδχΞ w Ϡϑʔͷࢠձࣾ
w +BWB4DSJQUͱ͔͕͖Ͱ͢ !2
13 w 3FBDUೖͱ͍͏ຊΛ ॻ͖·ͨ͠ w 3FEVYʹ͍ͭͯ ͪΌΜͱॻ͍͍ͯΔຊ ͓ͦΒ͘͜Ε͚ͩ w Α͔ͬͨΒങ͍ͬͯͩ͘͞
13 w 8&# %#13&44Ͱ ʮͲΜͱ͍͜ϑϩϯτΤϯυ ։ൃʯͱ͍͏࿈ࡌΛ͍ͬͯ ·͢ w Α͔ͬͨΒಡΜͰ͍ͩ͘͞ʂ
ΑΖ͓͘͠ئ͍͠·͢ !
8FC1VTIΛ ʢͪΐͬͱʣৄ͘͠આ໌͢Δ
͢͜ͱ w 8&# %#13&44WPMͰ 18"ʹؔ͢ΔهࣄΛॻ͍ͨ w จྔͷؔͰɺ ୲ͨ͠8FC1VTIͷ෦Λ ͔ͳΓͬͨ w
͏ͪΐͬͱ͚ͩৄ͘͠ ղઆ͍ͤͯͩ͘͞͞
8FC1VTI w 8FCͰϓογϡ͕ग़དྷΔ w ϒϥβଆͷ"1*8$͕ɺ ϓογϡ͢Δଆͷϓϩτίϧ*&5'͕ඪ४Խ͍ͯ͠Δ w 18"$IFDLMJTUʹϓογϡ௨ͷ߲͕͋Γɺ 8FC1VTI18"ͷҰ෦ͱݴͬͯͳ͍ʂʂʂʂ ʢ18"ͬΆ͘ͳ͍ൃදͰେมਃ͠༁ͳ͍"ʣ
!8
ϒϥβଆͷ"1*
w TDSJQU w ߪಡͷ։࢝ w TFSWJDF8PSLFS3FHJTUSBUJPOTVCTDSJCF \ʜ^ w 4FSWJDF8PSLFS
w ϓογϡͷड৴ w TFMGBEE&WFOU-JTUFOFS QVTI ʜ !10
1 // <script>でロード・実行されるJavaScript 2 3 // サポート判定 4 if (!('serviceWorker'
in navigator && 'PushManager' in window)) { 5 console.log('Web push is not supported in this browser'); 6 return; 7 } 8 9 // ServiceWorkerの登録 10 // ServiceWorkerRegistrationを返す 11 const reg 12 = await navigator.serviceWorker.register('./sw.js'); 13 14 // 購読開始 15 // ユーザに許可を求めるダイアログが表示され、 16 // 許可されればPushSubscriptionを返す 17 const pushSubscription = await reg.pushManager.subscribe({ 18 // プッシュサーバーの公開鍵 19 applicationServerKey, 20 21 // "返されたプッシュサブスクリプションの効果が 22 // ユーザーに表示するメッセージにだけ使われるかを示す" 23 // https://mzl.la/2GgFKbs 24 userVisibleOnly: true 25 }); !11
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
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
ϓογϡΛૹ৴͢Δ ॲཧ
w XFCQVTIMJCTʹϥΠϒϥϦ͕͋Δ w IUUQTHJUIVCDPNXFCQVTIMJCT w ࠓճ/PEFKT൛Ͱ࣮ w IUUQTHJUIVCDPNXFCQVTIMJCTXFCQVTI w OQNJOTUBMMTBWFXFCQVTI
03 ZBSOBEEXFCQVTI !15
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
1 // 送信するデータ 2 const message = { … }
3 4 // メッセージを送信 5 webpush.sendNotification( 6 // ブラウザから受け取ったPushSubscription 7 pushSubscription, 8 // 文字列 9 JSON.stringify(message) 10 ); !17
σϞ
͏ͪΐͬͱৄ͘͠
ͬ͘͟Γͱͨ͠શମ૾ 1VTI4FSWJDF 4FSWFS TVCTDSJCF 1VTI4VCTDSJQUJPO 1VTI4VCTDSJQUJPO 4FOENFTTBHF 4FOENFTTBHF "QQ !20
αʔόʔͷೝূ 1VTI4FSWJDF ਖ਼͍͠4FSWFS ѱ͍4FSWFS "QQ 4FOENFTTBHF 4FOENFTTBHF 0,# /($ !21
αʔόʔͷೝূ 1VTI4FSWJDF ਖ਼͍͠4FSWFS ॺ໊Λ༩ ॺ໊Ͱ ਖ਼͍͠4FSWFS͔Βͷ ϝοηʔδͰ͋Δ͜ͱΛ֬ೝ "QQ 0,# !22
αʔόʔͷೝূ 1VTI4FSWJDF ਖ਼͍͠4FSWFS ॺ໊Λ༩ ॺ໊Ͱ ਖ਼͍͠4FSWFS͔Βͷ ϝοηʔδͰ͋Δ͜ͱΛ֬ೝ "QQ 0,# 7"1*%
!23
7"1*% w IUUQTUPPMTJFUGPSHIUNMSGD w 7PMVOUBSZ"QQMJDBUJPO4FSWFS*EFOUJpDBUJPO GPS8FC1VTI w 8FC1VTIͷαʔόೝূ7"1*%Λࢼͯ͠ΈΔ چ($. ͷొ͕ෆཁʹͳͬͨ$ISPNFͷ8FC1VTIΛࢼͯ͠ΈΔ
2JJUB !24
1VTI4FSWJDF 4FSWFS "QQ ެ։伴 ൿີ伴 ᶃ伴ϖΞΛੜ ᶄࣄલʹެ։伴Λڞ༗ ᶅTVCTDSJCF࣌ʹ ϓογϡαʔϏεʹެ։伴Λૹ৴ ᶆൿີ伴Ͱॺ໊Λ࡞
+85 +85 ᶇຊจʴॺ໊ɺެ։伴Λૹ৴ ᶈೝূ͢Δ !25
ᶃ伴ϖΞΛੜ !26
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
ᶄࣄલʹެ։伴Λڞ༗ ᶅTVCTDSJCF࣌ʹ ɹϓογϡαʔϏεʹެ։伴Λૹ৴ !28
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
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
ϖΠϩʔυͷ҉߸Խ 1VTI4FSWJDF ਖ਼͍͠4FSWFS "QQ ҉߸Խ ෮߸ ҉߸Խ͞Ε͍ͯΔͷͰɺ 1VTI4FSWJDFதΛݟΕͳ͍ !31
ͳͥ҉߸Խ͕ඞཁ͔ʁ w 8FC1VTIΞϓϦέʔγϣϯ͕ϓογϡαʔϏεΛ ҙࣝ͠ͳ͍ͰΑ͍ઃܭʹͳ͍ͬͯΔ w ϓογϡαʔϏεͷ"1*͕ಉ͡ͳͷͰ w ໘ɺ͔ͨ͠͠Β৴པͰ͖ͳ͍ϓογϡαʔϏεʹ σʔλΛૹ৴͍ͯ͠Δ͔͠Εͳ͍ w
ΞϓϦͷΫϥΠΞϯτɾαʔόҎ֎ ϝοηʔδ͕ಡΊͳ͍Α͏ʹ͖͢ !32
҉߸Խʹؔ͢Δ༷ w .FTTBHF&ODSZQUJPOGPS8FC1VTI IUUQTUPPMTJFUGPSHIUNMSGD w 3'$ˣΛͬͯ҉߸Խ͢ΔखॱͳͲ w &ODSZQUFE$POUFOU&ODPEJOHGPS)551 IUUQTUPPMTJFUGPSHIUNMSGD w
)551্ͷϖΠϩʔυͷ҉߸Խɺ伴ަͳͲ !33
ͬͱৄ͘͠ w 5IF8FC1VTI1SPUPDPMc(PPHMF%FWFMPQFST IUUQTEFWFMPQFSTHPPHMFDPNXFCGVOEBNFOUBMT QVTIOPUJpDBUJPOTXFCQVTIQSPUPDPM w ӳޠ͕ͩίʔυ͖ͰΠϝʔδ͍͢͠ w <վగ൛>8FC1VTIͰϒϥβʹϓογϡ௨ΛૹͬͯΈ Δ2JJUB
w ༷ͷߋ৽ʹै͓ͯ͠Γɺྑهࣄ !34
͓ΘΓʹ
ϓϩτίϧʹ͍ͭͯ w ϓϩτίϧ͕γϯϓϧˍϥΠϒϥϦ͕͋ΔͷͰɺ 1BB4ͳͲʹཔΒͣͱࣗલͰͳΜͱ͔ͳΔ w ·༷͕ͩมΘΔՄೳੑ͕͋Γͦ͏ͳͷͰɺ ༷ϒϥβʔͷ࣮ঢ়گΛ͍ͳ͕ΒʹͳΔ !36
༷ΛͪΌΜͱௐͯΈͯ w ϓϩτίϧ͕γϯϓϧͳͷͰɺ ؤுΕશ෦ͷ༷ॻΛಡΊΔ ʢ࣮ࡍʹ̍िؒҐͰ͘Β͍Λ௨ͤͨʣ w ༷ॻΛಡΉͱʮಡΜͩͧʯͱ͍͏ؾ࣋ͪʹͳΓɺ ʢʹཱ͔ͭͱݴΘΕΔͱඍົ͕ͩʣṖͷຬײ͕͋Δ w ͕ɺ҉߸Խɾ伴पΓௐ࢝ΊΔͱ
ͲΜͲΜΘ͔Βͳ͍͜ͱ͕ग़ͯ͘ΔͷͰ ·ͱ·͕ͬͨ࣌ؒඞཁ͔͠Εͳ͍ !37
ऴ