Node.jsとExpressにおけるCookieを利用したセッション管理を行う方法を紹介します。
express-sessionを利用する
ExpressでCookieによるセッション管理を行う場合に利用されるモジュールは以下の2つです。
モジュール | セッションデータの保存場所 |
---|---|
express-session | サーバー側のストレージ (オンメモリ/外部ストレージ) |
cookie-session | Cookie |
今回は以下の理由からexpress-sessionを採用することにします。
- Cookie内には少量の単純なデータ(プリミティブな値)のみしか保存できない
- セッションデータがブラウザから閲覧出来てしまう
express-session+Redisを実装する
それでは実装例を紹介します。express-sessionはデフォルトではサーバーメモリ上でセッションデータを保管しますが、実際の環境での稼動向きではないため通常外部ストレージを利用します。
今回はRedisを導入しますが、動作検証だけしたい方はストレージなしでも稼働確認できるので以下の事前準備はスキップして問題ありません。
(事前準備)Redisの用意
まずは事前準備としてRedisをDockerコンテナで準備します。
以下のdocker-compose.yaml
ファイルを作成し、docker-compose up -d
コマンドを実行してコンテナを起動します。
version: '3.8'
services:
redis:
image: redis:latest
restart: always
ports:
- 6379:6379
必要なモジュールのインストール
実装に必要なモジュールをインストールします。
npm i express-session
# redisを使って実装を行う人
npm i express-session redis connect-redis
# さらにTypescriptを利用している人は追加で以下のコマンドを実行
npm i -D @types/express-session @types/connect-redis
express-sessionの利用
express-sessionはsessionミドルウェアを利用することでセッションを簡単に管理することが出来ます。
import express from 'express';
import session from 'express-session';
import redis from 'redis';
const RedisStore = require('connect-redis')(session);
const redisClient = redis.createClient('redis://localhost:6379');
const app = express();
app.use(
session({
name: 'mockSessionId',
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store: new RedisStore({ client: redisClient }),
cookie: {
secure: false,
httpOnly: false,
},
})
);
オプションの概要は以下の通りです。
オプション | 説明 |
---|---|
name | セッションIDの名前(デフォルトではconnect.sid) |
secret | Cookieの署名に利用するキー クライアントには[セッションID].[secretを利用した署名]の値が返される |
resave | リクエスト中にセッションデータが書き換えられなかったとしてもストレージに保存するか。 |
saveUninitialized | 初期化されていない(何も入っていない)セッションに値を入れてストレージにほぞんするか |
store | セッションデータを保管するストレージを指定する。 指定しない場合はオンメモリで管理される。 |
cookie | Cookieに関するオプション。 |
これで基本的な準備は完了です。
動作確認
それでは実際に動作を確かめてみます。サンプルとしてexpress–sessionに掲載されている例をredis対応したものを使用します。
ソースコードはこちらに掲載しています。
リクエストを受けるとセッションを生成し、セッションデータとして各ユーザーのリクエスト回数を記録してレスポンスとして返却するというシンプルなアプリケーションです。
初回リクエスト
まずはシンプルに初回リクエストを送ってみます。
レスポンスに”you viewed this page 1 times”が返ってきました。
% curl -v http://localhost:3000/foo
* Trying 127.0.0.1:3000...
・・・
< HTTP/1.1 200 OK
・・・
< Set-Cookie: mockSessionId=s%3AKq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj.ODOZ3MifiQcMVp0CYjA7v4XrYtuqhOHRsD2pcs0DjaE; Path=/
・・・
you viewed this page 1 times
レスポンスヘッダーにはSet-Cookieに署名付きのセッションID(Kq…PVj)が格納されていることが確認できます。
redisの中も確認してみましょう。セッションIDがキーとして格納されていることが確認できます。
セッションデータにはアプリケーションへのリクエスト回数viewが格納されている事が確認できます。
root@4d7c8780bc68:/data# redis-cli
127.0.0.1:6379> keys *
1) "sess:Kq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj"
127.0.0.1:6379> get sess:Kq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj
"{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":false,\"path\":\"/\"},\"views\":{\"/foo\":1}}"
Cookieをセットして2回目のリクエスト
続いて2回目のリクエストを行います。ブラウザでは通常送られたCookieを自動で保存して同じドメインに対するリクエストに対しては保存したCookieをセットしてリクエストが行われます。
今はcurlでリクエストを行っているので手動でCookieをセットしてリクエストしてみます。
% curl -b "mockSessionId=s%3AKq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj.ODOZ3MifiQcMVp0CYjA7v4XrYtuqhOHRsD2pcs0DjaE" http://localhost:3000/foo
・・・
you viewed this page 2 times
レスポンスのリクエスト回数が1から2に変化しました。これはサーバー側がリクエストCookieのセッションIDを理解して、redisのセッションデータを参照して状態を更新したことになります。
redis内を確認するとしっかりとviewsが1から2に更新されていることがわかります。
127.0.0.1:6379> get sess:Kq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj
"{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"secure\":false,\"httpOnly\":false,\"path\":\"/\"},\"views\":{\"/foo\":2}}"
Cookieをセットしないで3回目のリクエスト
では最後にCookieをセットしないで3回目のリクエストを行ってみます。
レスポンスのリクエスト回数は3ではなく1となってしまいました。レスポンスヘッダーのSet-Cookieにも異なるセッションIDが振られています。
% curl -v http://localhost:3000/foo
* Trying 127.0.0.1:3000...
・・・
< HTTP/1.1 200 OK
・・・
< Set-Cookie: mockSessionId=s%3AdSTw4B3PhdGNukAEoRxUCCxMPtdk35No.q5r%2BeI%2FqjfUCqRgSKrxKWDQuD1NHvwLqtzgglN2%2BbyY; Path=/
・・・
you viewed this page 1 times
これはCookieをセットしないでリクエストしたためにサーバー側は新しいユーザーのリクエストだと判断して新規のセッションを生成したことになります。
redisを確認しても新しいセッションが生成されていることが確認できます。
127.0.0.1:6379> keys *
1) "sess:dSTw4B3PhdGNukAEoRxUCCxMPtdk35No"
2) "sess:Kq861ZrP5PX_qLyE0TuAqJJcE3qN1PVj"
まとめ
以上のようにexpress-sessionを利用することでExpressアプリケーションでセッション管理を簡単に実装することができることを紹介しました。
今回はストレージとしてredisを利用しましたが、その他のストレージも利用できるのでご自身の環境に合わせてカスタマイズしてください。
コメント