Expressでログイン機能を作る

MacOSX上で、nvmを使ってインストールしたnode.js v0.4.9の環境です。

まず細かい設定はせずに、ざっくりとた例です。Expressのセッションサポートについてはこちらに記載されています。そのまま設定するとこんな感じ。

app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));

上記の設定をすると、req.sessionという値が利用できるようになります。ルーティングの例。

app.get('/', function(req, res){
  if (req.session.flg) {
    res.render('index');
  }
});

アクセスしただけでセッションidは発行されますが、セッションを張っただけでは、req.session.flgという値は無いのでページは見れません。idとpasswdの認証が通った後などに設定してあげる必要があります。

var id = 'foo';
var passwd = 'hoge';

app.post('/check', function(req, res) {
    if(id === req.body.id && passwd === req.body.pw) {
      req.session.flg = true;
      res.redirect('/');
    } 
});

テンプレートはこんな感じ。

h1= title
p Welcome to #{title}
h1= title
p Enter your id & pw 
form( method='POST', action='/check')
  input( type='text', name='id' )
  input( type='password', name='pw' )
  input( type='submit', name='enter', value='login' )

とりあえずこれだけで、ログインぽいものはできました。

デフォルトのまんまだとセッション情報は...メモリ上? にあるのかな。だとなんか不安なので、セッションストアを利用します。ドキュメントだとRedisがススメられていますが、アプリケージョンの情報はMongoDBを使っているので、セッションストアにもMongoDBを使いたいのです。使うとこんな感じです。

var Db = require('mongodb').Db;
var Server = require('mongodb').Server;
var server_config = new Server( 'localhost', 27017, {auto_reconnect: true, native_parser: true});
var db = new Db( 'authtest', server_config, {} );
var mongoStore = require( 'connect-mongodb');
var auth;
  app.use(express.cookieParser());
  app.use(express.session({
    cookie: {maxAge: 60000 * 20},
    secret: 'foo',
    store: auth = new mongoStore({db: db})
  }));

これで、セッションの置き場がMongoDBに置き換えられました。確認してみましょう。

$ mongo
MongoDB shell version: 1.8.0
connecting to: test
> use authtest
switched to db authtest
> db.sessions.find();
{ "_id" : "fWBduKOKEq0rnHc5YWp7UBQ3.uCg+n9B8B0Kx6KxPK7AaixqSrCArGGIgl3EAkiKm4+M", "session" : { "lastAccess" : NumberLong("1310397363006"), "cookie" : { "originalMaxAge" : 1200000, "expires" : "2011-07-11T15:36:03.007Z", "httpOnly" : true, "path" : "/" } }, "expires" : NumberLong("1310398563000") }
>

セッションストアを利用して認証状態を確認するときはこんな感じになるかと思います。

app.get('/', function(req, res){
  auth.get(req.session.id, function(err, sess) {
    if(sess && sess.views) {
      res.render('index', {
        title: req.session.userid
      });
    } else {
      res.redirect('/login');
    }
  });
});

app.post('/check', function(req, res) {
  User.findOne({id: req.body.id}, function(err, docs){
    if(docs !== null && docs.passwd === req.body.pw) {
      req.session.userid = req.body.id;
      req.session.views = 1;
      res.redirect('/');
    } else {
      res.render('login', {
        title: 'login',
        message: 'login form',
        error_message: 'password faild'
      });
    }
  });
});

このままだとログインしっぱなしなので、ログアウト機能も付けましょう。

app.get('/logout', function(req, res) {
  auth.destroy(req.session.id, function(err) {
    req.session.destroy();
    console.log('deleted sesstion');
    res.redirect('/');
  });
});

全ソースはこちら