Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
クロスオリジンでログイン状態を継続させるように見せるテクニック
Search
おdd
March 02, 2018
Programming
1
930
クロスオリジンでログイン状態を継続させるように見せるテクニック
in YAPC::Okinawa(2018) 前夜祭LT
おdd
March 02, 2018
Tweet
Share
More Decks by おdd
See All by おdd
学歴マイノリティの皆様に贈る布石の打ち方のススメ
oddmutou
1
330
Other Decks in Programming
See All in Programming
Androidアプリのモジュール分割における:x:commonを考える
okuzawats
1
190
Kaigi on Railsに初参加したら、その日にLT登壇が決定した件について
tama50505
0
110
CQRS+ES の力を使って効果を感じる / Feel the effects of using the power of CQRS+ES
seike460
PRO
0
160
見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理
kentaroutakeda
0
710
Jakarta EE meets AI
ivargrimstad
0
270
Online-Dokumentation, die hilft: Strukturen, Prozesse, Tools
ahus1
0
100
iOS開発におけるCopilot For XcodeとCode Completion / copilot for xcode
fuyan777
1
110
ゆるやかにgolangci-lintのルールを強くする / Kyoto.go #56
utgwkk
2
450
フロントエンドのディレクトリ構成どうしてる? Feature-Sliced Design 導入体験談
osakatechlab
8
4.1k
Semantic Kernelのネイティブプラグインで知識拡張をしてみる
tomokusaba
0
180
DevFest - Serverless 101 with Google Cloud Functions
tunmise
0
120
103 Early Hints
sugi_0000
1
260
Featured
See All Featured
Unsuck your backbone
ammeep
669
57k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.1k
Docker and Python
trallard
42
3.1k
Optimizing for Happiness
mojombo
376
70k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.3k
How to Ace a Technical Interview
jacobian
276
23k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
159
15k
Fantastic passwords and where to find them - at NoRuKo
philnash
50
2.9k
A better future with KSS
kneath
238
17k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
44
9.3k
The Language of Interfaces
destraynor
154
24k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
251
21k
Transcript
クロスオリジンで ログイン状態を継続させる ( ようにみせる ) テクニック not SSO with perl(sledge)
and JS(jquery) 2018/03/02 YAPC::Okinawa 前夜祭 Nao Muto (@oddmutou)
自己紹介 氏名生年月日ハンドル 武藤 尚 Muto Nao (1996/06/25) Twitter 等 :
@oddmutou CPAN: MUTO 所属等 シーサー株式会社 社員 ( したらば掲示板, SesaaWiki , BoomAppGames , etc.) 東京デザインテクノロジーセンター専門学校 非常勤講師 東京通信大学 学生 ( 春から )( 第一期生予定 )( ネタ入学で差をつけろ )
とある新規サービス 仕様策定・設計等 他社競合 ? を見ると表示側はサブドメインを沢山つくってるしうちもそうしよう → 「そのほうがかっこよさそう ! 」( SEO
的にはどうなんかよくしらんけど) dev.to 流行ってるし CDN で爆速配信!目標の月間 ? 億 PV に耐えれるインフラも ! → 「やるぞ ! 」 (dev.to が話題になるあたりに始めて年内リリース済みの爆速開発 ) (インフラ設計も俺) SNS 連携 / 会員登録的な機能も作ろう → 「テンション上がってきた ! 」「まぢ卍」
雑な構成図 なんとか .sample.com user.sample.com 新規登録 / ログイン / ログアウト アカウント設定
アカウントへの通知なども - A.sample.com - B.sample.com - C.sample.com … 表示がメイン 裏側で使うようの何か .sample.com DB (記事データ等)や KVS (セッション等) ◦ 各 にはそれぞれ別のアプリが (マイクロサービス化的なことやってみたかったんです)
主なページは CDN でゴリゴリキャッシュさせるから静的 しかし右上にログイン状態を表示させたい ! github や twitter とかによくある,非ログイン時ならログインや新規登録へのリンクが,ロ グイン時ならアイコンがでるやつ。
→JS で取れるようにユーザー情報管理サーバーにログイン状態取得 API を作る →REST API? そんなのしらないヨ ということでセッションクッキーを見ちゃう $.ajax({ url:
‘https://user.sample.com’, type: 'GET', dataType: 'json', xhrFields: { withCredentials: true } }).done(function(response){ 以下略 「 xhrFields: {withCredentials: true} 」で cookie 送ってくれるのかーなるほどー
複数ドメイン間 (A.example.com → user.example.com) で API 叩くには Access- Control-Allow-Origin が必要というのはわかっていた。
また,今回はログイン状態を取るだけであるため,厳しく設定する必要もないので,「 * 」に する。 $self->r->header_out('Access-Control-Allow-Origin' => ‘*’);
> クロスオリジン要求をブロックしました : 同一生成元ポリシーによ り、 https://user.sample.com にあるリモートリソースの読み込みは拒否されます ( 理由 :
CORS ‘ ヘッダー Access-Control-Allow-Credentials’ ‘ は true’ である べき ) 。 ドメインが異なる API のときは「 Access-Control-Allow-Origin: * 」ってつけてれば良い ことは知っていたが, Cookie 送るときはまた別のものが要るらしい。 $self->r->header_out('Access-Control-Allow-Origin' => ‘*’); $self->r->header_out('Access-Control-Allow-Credentials' => "true");
> クロスオリジン要求をブロックしました : 同一生成元ポリシーにより https://user.example.com にあるリモートリソースの読み込みは拒否されます ( 理 由 :
CORS ‘ ヘッダー Access-Control-Allow-Origin’ ‘ が *’ である場合、認証情 報はサポートされない ) 。 Cookie 送るときは「 Access-Control-Allow-Origin: * 」だと怒られるらしいので,もう無 … 理やり 。 とりあえずこれで取得できて,超いい感じに表示できた。 $self->r->header_out('Access-Control-Allow-Origin' => $self->r->header_in('Origin')); $self->r->header_out('Access-Control-Allow-Credentials' => "true");
毎回 API 叩くのは負担になるからブラウザ側でキャッシュしたい ! →sessionStorage に保管する →sessionStorage は KVS なので出し入れ時に
JSON にデコード / エンコードする。 → 普段 SSR なサービスしか触ってないと知らないことだったけど,かんたん。 sessionStorage.setItem('login_status', JSON.stringify(response)); JSON.parse(sessionStorage.getItem('login_status')));
キャッシュクリアのタイミングどうする ? → sessionStorage なのでウィンドウ閉じればクリアされるが,それ以外に も, user.example.com でのログイン時・ログアウト時にキャッシュをクリアする必要があ る。 →
cookie のように「 *.example.com 」のようにスコープにワイルドカードを使いたいが, sessionStorage のスコープは「発生元のオリジン」 ( スキーム & ドメイン & ポート ) で固 定されており, user.example.com から A.example.com の sessionStorage を操作す ることはできない。
革命的発案 *.example.com な cookie に「ログイン状態の最終更新日時」を保管し,それを読んで sessionStorage を操作する &API を叩いて更新すればよい user.example.com
ログイン,ログアウト時に cookie を更新 $.cookie('update_time', 今の時間 , {path: '/', domain: '*.example.com'}); A.example.com ログイン,ログアウト時に cookie を更新 last_time = localStorage.getItem(‘last_time’;) time = $.cookie('update_time'); if (time > last_time) { localStorage のキャッシュを使う } else { API 叩く &localStorage のキャッシュを更新 localStorage.setItem(‘last_time’, 今の時間 ); } ↑ 「時間」の中身は端折りましたが,私は unix 秒 ( 整数値 ) を使いました。また jquery.cookie.js を使っていま
全く別ドメインのサービスでもユーザー情報使いたい ! → 敗北宣言 :iframe 使います user.example.com ログイン・ログアウトページ iframe 内の
hogehoge.com $.cookie('update_time', 今の時間 , {path: '/', domain: 'hogehoge.com'}); hogehoge.com 先ほどの A.example.com と同様に, cookie の update_time を見て localStorage のキャッシュを使うか, API 叩いて更新するかします。
ぼく「クロスオリジン,気が狂いそうです〜 ( 普段単純な SSR しかやらないので ) 」 先輩「広告やさんっぽいことやってるね〜」
似た話が別サービス(以下サービス A とします) で湧いてきた
ユーザーで独自ドメインが設定できるサービス A ↓ 記事ページにもログインしてコメントのフォームが欲しい ! ユーザーの独自ドメイン 適用ページ - トップページ -
記事ページ 等表示だけのページ & サーバー側でキャッシュも効いている 独自ドメイン 非適用ページ - コメントページ 等ログインして書き込みとかできるページ
サービス A において,静的ページでもログイン状態を表示したい場合は,セッション用の cookie が存在すれば API を都度叩く,という実装が既にあった。 ( 想定アクセス数的にキャッシュするほどでもないが,クローラーや一見さんの閲覧時に 無駄に
API 叩かれるのは怖いため。 ) ↓ 独自ドメインの場合はセッションが無いので,別途ログインするユーザーか見分ける印を 付ける必要がある。 そして歴史あるサービスであるため,深く弄りだすと年が暮れるので避けたい。
〜サービス A のログイン時の挙動〜 独自ドメインページ(ユーザーのドメイン)の「ログイン」リンクをクリック ↓ サービス A のデフォルトドメイン(弊社のドメイン) ↓ リダイレクト
弊社の複数サービスで使っているアカウントサーバー ↓ リダイレクト サービス A のデフォルトドメイン ↓ リダイレクト 独自ドメイン(「ログイン」リンクがあったページに戻る)
〜サービス A のログイン時の挙動〜 独自ドメインページ(ユーザーのドメイン)の「ログイン」リンクをクリック ↓ サービス A のデフォルトドメイン(弊社のドメイン) ↓ リダイレクト
弊社の複数サービスで使っているアカウントサーバー ↓ リダイレクト サービス A のデフォルトドメイン ↓ リダイレクト 独自ドメイン(「ログイン」リンクがあったページに戻る) ここにログインしていることを示す SessionStorage 保管する処理を追加
〜サービス A のログイン時の挙動〜 独自ドメインページ(ユーザーのドメイン)の「ログイン」リンクをクリック ↓ サービス A のデフォルトドメイン(弊社のドメイン) ↓ リダイレクト
弊社の複数サービスで使っているアカウントサーバー ↓ リダイレクト サービス A のデフォルトドメイン ↓ リダイレクト 独自ドメインで Cookie を操作 ↓ リダイレクト 独自ドメイン(「ログイン」リンクがあったページに戻る)
〜サービス A のログイン時の挙動〜 独自ドメインページ(ユーザーのドメイン)の「ログイン」リンクをクリック ↓ サービス A のデフォルトドメイン(弊社のドメイン) ↓ リダイレクト
弊社の複数サービスで使っているアカウントサーバー ↓ リダイレクト サービス A のデフォルトドメイン ↓ リダイレクト 独自ドメインで Cookie を操作 ↓ リダイレクト 独自ドメイン(「ログイン」リンクがあったページに戻る)
まとめ - クソにクソの上塗りをするのはいつも悔しい - どこでローカルに仕込むとか考えるのなんだかんだで楽しい! - Perl で単純に SSR なサービスしか書かないマン,たまにこういうことやると楽しい!