Help us understand the problem. What is going on with this article?

Node.js + Express.js + express-sessionでセッションにデータ格納する方法

More than 3 years have passed since last update.

express-sessionがバージョンアップしているせいか、ネット上のサンプル見て実装してたらハマったのでメモ。

作るもの

  • express-generatorで作成した雛形をベースにする
  • ユーザ認証はしない(別の機会に)
  • セッションストアはひとまずメモリストア(デフォルト、再起動で初期化)
  • アプリケーションルートにアクセスした時に、ユーザ名がセッションに無かったらログイン画面(というかユーザ名入力画面)にリダイレクトさせる
  • ユーザ名が入力されたらセッションに格納し、アプリケーションルートにリダイレクトさせる

SPAなWEBアプリケーションにユーザ入力を挟むようなルーティングのサンプルです。

出来上がったもの

githubにあげておきました。
このコミットでの差分を見れば作業内容は把握できるかと思います。

https://github.com/moomoo-ya/express-session-sample

作業の手順

expressコマンドで雛形作成

$ express [アプリケーション名]

サンプルで--hbsとか--ejsとかしていないので、そういう方は適宜読み替えてください。
あまり重要な部分ではないです。

app.jsを修正

app.jsの修正が一番手間な部分です。

モジュールの読み込み

app.js
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session'); // 追加

express-sessionを読み込みます。

ルーティング定義ファイルを読み込み

app.js
var routes = require('./routes/index');
var users = require('./routes/users');
var login = require('./routes/login');  // 追加

ルーティングを実装する予定のroutes/login.jsを読み込みます。

express-sessionモジュールを設定

app.js
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

// 以下追加 
app.use(session({
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: false,
  cookie: {
    maxAge: 30 * 60 * 1000
  }
}));

session(=express-session)を利用する設定をします。

secretはCookieの暗号化に利用するキーなので適宜変更して利用します。アプリケーションリリース後に変更したい場合は、
secret: ['(新しいキー)', '(今までのキー)', '(今までのキー)']
とすることで先頭のキーを暗号化に用いて、復号化には配列中のいずれかのキーを利用するらしいです(未検証)。

resaveはtrueだとセッションストアにアクセスするたび(=セッションチェックする領域にリクエストするたび)にセッションを作り直すらしいので、基本はfalseでOKかと思います。

saveUninitializedはtrueだと未初期化状態のセッションも保存されるようになるようですが、一般的に使われるログインセッションの処理では未初期化のセッションは不要なので、falseでOKかと思います。

resaveとsaveUninitializedの挙動は今ひとつ自信がない……。

cookie.maxAgeはCookieの有効期限をミリ秒で設定します。指定なし、もしくはnullだとブラウザデフォルトの挙動(一般的にはブラウザを閉じたらCookie削除)になります。

セッションチェック処理を実装

この部分はmiddlewareとして別ファイルに分割した方がスマートかもですね。

app.js
var sessionCheck = function(req, res, next) {
  if (req.session.user) {
    next();
  } else {
    res.redirect('/login');
  }
};

セッション情報(req.session.*、今回はreq.session.userに格納されている)の有無をチェックして後処理に継続next()するか、ログイン画面にリダイレクトするか分岐させています。

ルーティングの追加とセッションチェック処理の追加

app.js
app.use('/login', login);  // 追加
app.use('/', sessionCheck, routes);  // sessionCheckを前処理に追加
app.use('/users', users);

app.use([path], function[, function...])のpathの部分は指定したパス以下に適用してしまうため、app.use('/login', login);app.use('/', sessionCheck, routes);よりも前に追加しないと、sessionCheckが適用されてしまい永遠にログイン画面にリダイレクトが発生してエラーになります。

ログイン処理のルーティングを定義

app.use('/login', login);から呼ばれているので'/'が'/login'を表すことになります。

routes/login.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
  res.render('login');
});

router.post('/', function(req, res, next) {
  if(req.body.userName) {
    req.session.user = {name: req.body.userName};
    res.redirect('../');
  } else {
    var err = '入力が正しくありません。確認して再入力してください。';
    res.render('login', {error: err});
  }
});

module.exports = router;

router.get('/', ...)の部分はログイン画面('/login')へのアクセスなので単純にlogin.jadeを表示するだけです。

router.post('/', ...)の部分はログイン画面のFORMからPOSTされてアクセスされます。
ここではreq.body.userNameの有無のみで分岐していますが、パスワード認証の場合はreq.body.passwordなども参照してDB参照した上で分岐になります。

req.session以下にデータを格納することでセッションへのデータ格納が実現できます。

入力データが正しくない場合はエラーメッセージを用意してlogin.jadeに渡します。

ログイン画面の実装

エラー表示と/loginへのPOSTのみなので参考までに。

views/login.jade
extends layout

block content
  h1 Login
  p.caution= error
  form(method='POST',action='/login')
    input(type='text',name='userName',placeholder='username')
    input(type='submit',value='Login')

パスワード認証の場合は
input(type='password',name='password')
などを適宜追加してください。


セッションストアはメモリストア以外を使ったほうが実用的と思うので、大規模システムならredisなど、スケール考えつつ手軽に始めるならnedb(mongo互換のファイルDB)などにしておいたほうが良いと思います。

「express-sessionモジュールを設定」の部分で設定追加することで出来るので、気が向いたらまた記事にします。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした