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


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

勉強中なので自分が理解出来るように解説してみます．
参考: [Expressでログイン機能を作る - uchida75cmの日記](http://uchida75cm.hatenablog.com/entry/20110712/1310469113)
参考: [Node.js + Express でログイン認証機能を実装する - Devlog](http://kkurahar.github.io/blog/2013/05/10/node-express-session/)

##バージョン
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です．
![](http://gyazo.com/b56d60a15ce131e17c3627770cfda865.png)

##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);`の前に以下を追記します．


```js:
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にリダイレクトさせます．ルーティング設定の前に記述しています．

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

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

```js:
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を呼び出せるようにしています．

```js:
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()などで確認してみるとわかると思います．

```js:
/*モデル読み込み*/
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
ログイン前のページです．

```html:
<!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
![](http://gyazo.com/ea4513ce614f3f4bc73971c5b00e8e6b.png)

###views/index.ejs
ログイン後のページです．

```html:
<!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
![](http://gyazo.com/88e3b2db0ba42ce265c34bc63e97440d.png)

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

お疲れ様でした〜．