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.3k
書き換えて学ぶTemporal #fukts
pirosikick
2
510
fukuoka.ts #3 社内でESLintの共通設定を配りたい2025年春版
pirosikick
3
510
compilerOptions、全部読んだ
pirosikick
1
310
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
Workers Cache を知る
syumai
0
330
JRuby: Past, Present, and Future
headius
0
220
Agentic Software Factoryに、すごく賢いIF文を。 / super smart IF statement into the Agentic Software Factory.
rkaga
7
1.8k
パズルゲームの作り方 / how to make puzzle games
kaityo256
PRO
2
270
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
230
Ghostty + Neovimで作る 透明でカッコ良い開発環境
j341nono
0
150
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
430
ゲームコントローラやキーボードのファームウェアをSwiftで書く
kishikawakatsumi
1
270
仕様駆動開発による爆速プロダクト開発 / Bakusoku Spec Driven Development
kobakei
0
170
見えないものを探る要求要件定義に必要な基本的思考 / invisible-requirement-thinking
minodriven
13
6.4k
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
360
Atomic Design, Enforced: Scaling Mobile Design Systems (next.app devCon / droidCon Berlin 2026)
steliosf
PRO
0
110
Featured
See All Featured
Paper Plane (Part 1)
katiecoart
PRO
2
11k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
3.2k
How to Ace a Technical Interview
jacobian
280
24k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
540
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
Digital Ethics as a Driver of Design Innovation
axbom
PRO
1
450
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
570
Darren the Foodie - Storyboard
khoart
PRO
4
4k
The SEO Collaboration Effect
kristinabergwall1
1
590
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
530
Marketing to machines
jonoalderson
1
5.8k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
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
ऴ