Node.js+Express+MongoDBでSessionを利用してログイン機能を実装

More than 1 year has passed since last update.

Node.jsでのセッションの使い方の勉強です.
jadeでやってるサンプルが多いですが,jadeはイマイチ直感的に書けない(慣れない)のでテンプレートエンジンはejsを使います.

勉強中なので自分が理解出来るように解説してみます.
参考: Expressでログイン機能を作る - uchida75cmの日記
参考: Node.js + Express でログイン認証機能を実装する - Devlog

バージョン

node v0.10.23
express 3.4.8
npm 1.3.17
MongoDB shell version: 2.4.8

まずはプロジェクト作成

$ express -e login
$ cd login
$ npm install
$ node app.js

とりあえずブラウザからhttp://lobalhost:3000にアクセスして確認してみましょう.エラーが無く,Expressの文字が表示されればOKです.

MongoDB関連モジュールのインストール

MongoDBを利用するためのモジュールをインストールしておきましょう.調べると出てきますがmongooseはよく使われているみたいです.

$ npm install mongoose
$ npm install connect-mongo

MongoDB起動

ターミナルをnode.jsとは別プロセス(別なタブやウィンドウ)で起動しましょう.
プロジェクトフォルダ内にdbフォルダを作成し,--dbpathオプションでdbフォルダを保存先に指定するようにしました.

$ mkdir db
$ mongod --nojournal --noprealloc --dbpath db

app.js

connect-mongoの読み込み

app.jsの上部のrequire()の部分に以下を追記します.

var MongoStore = require('connect-mongo')(express);

セッションを使う準備

sessionを使うためにはcookieを読み込む必要があるらしいのでapp.use(app.router);の前に以下を追記します.

app.use(express.cookieParser()); //追加
app.use(express.session({
    secret: 'secret',
    store: new MongoStore({
        db: 'session',
        host: 'localhost',
        clear_interval: 60 * 60
    }),
    cookie: {
        httpOnly: false,
        maxAge: new Date(Date.now() + 60 * 60 * 1000)
    }
})); //追加

認証用のバリデーター関数

セッションに値が無ければ/loginにリダイレクトさせます.ルーティング設定の前に記述しています.

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

ルーティング設定

ルーティング設定はこんな感じです.↑で記述したloginCheck関数を咬ますことで認証必須のページと認証無しで見れるページを切り分けます.

app.get('/', loginCheck, routes.index);
app.get('/login', routes.login);
app.post('/add', routes.add);
app.get('/logout', function(req, res){
  req.session.destroy();
  console.log('deleted sesstion');
  res.redirect('/');
});

model

app.jsと同じ階層にmodel.jsを作成しましょう.
Model定義をしています.
-db名:user
-collection名:info(UserSchema)

最後の行のexports.Userで他のファイルからModelを呼び出せるようにしています.

var mongoose = require('mongoose');
var url = 'mongodb://localhost/user';
var db  = mongoose.createConnection(url, function(err, res){
    if(err){
        console.log('Error connected: ' + url + ' - ' + err);
    }else{
        console.log('Success connected: ' + url);
    }
});

// Modelの定義
var UserSchema = new mongoose.Schema({
    email    : String,
    password  : String
},{collection: 'info'});

exports.User = db.model('User', UserSchema);

controller

routes/index.js

var model = require('../model.js')で↑で定義したmodelの情報を読み込みます.
User = model.User;とすることで↑のUserSchemaがroutes/index.jsでも使えるようになります.

また,sessionの情報はreq.sessionに入っているのでconsole.log()などで確認してみるとわかると思います.

/*モデル読み込み*/
var model = require('../model.js'),
    User  = model.User;

/*ログイン後ページ*/
exports.index = function(req, res){
    res.render('index', { user: req.session.user});
    console.log(req.session.user);
};

/*ユーザー登録機能*/
exports.add = function(req, res){
    var newUser = new User(req.body);
    newUser.save(function(err){
        if(err){
            console.log(err);
            res.redirect('back');
        }else{
            res.redirect('/');
        }
    });
};

/*ログイン機能*/
exports.login = function(req, res){
    var email    = req.query.email;
    var password = req.query.password;
    var query = { "email": email, "password": password };
    User.find(query, function(err, data){
        if(err){
            console.log(err);
        }
        if(data == ""){
            res.render('login');
        }else{
            req.session.user = email;
            res.redirect('/');
        }
    });
};

view

あとはフォームなどを作ればOKです.

views/login.ejs

ログイン前のページです.

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>ログイン前</title>
</head>
<body>
  <h2>ログイン</h2>
  <form action="/login" method="GET">
    <input type="text" name="email" placeholder="Email"/>
    <input type="password" name="password" placeholder="Password"/>
    <input type="submit">ログイン</button>
  </form>

  <h2>新規登録</h2>
  <form action="/add" method="POST">
    <input type="text" name="email" placeholder="Email"/>
    <input type="password" name="password" placeholder="Password"/>
    <input type="submit">新規登録</button>
  </form>
</body>
</html>

画面例 /login

views/index.ejs

ログイン後のページです.

<!doctype html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ログイン後</title>
</head>
<body>
    <h1>My Site</h1>
    <p>Welcome to My Site</p>
    <%= user %> さんようこそ。
    <p>
        <a href="/logout">ログアウト</a>
    </p>
</body>
</html>

画面例 /index

動作確認

ログイン前に/ or /indexにアクセスしようとするとリダイレクトで/loginが表示されると思います.新規登録(特に何も表示されませんが)をしてから新規登録したメールアドレスとパスワードでログインをすると/を見れるはずです.また,ログアウトのリンクを押すとsessionが破棄され/logoinに戻ると思います.

お疲れ様でした〜.