Node.js + Passport + MySQL でセッション認証

https://github.com/pospome/NodePassportSample

サンプルプログラムです。
これを見ながら読み進めると分かりやすいかもしれません。

passportは認証対応しているサービスが沢山あり、
それらがモジュール化されているので、
必要な認証に対応するモジュールをインストールすることで動作する。

今回は一般的なセッション認証を利用する。
本来はユーザー情報をDBに保存するが、
一旦ログインアカウントを固定値にして、
passportの動作を確認する。

まずはインストール

# npm install --save express-session
# npm install --save passport
# npm install --save passport-local
# npm install --save connect-flash

*connect-flashはエラーなどのフラッシュメッセージを表示する際に利用するモジュール。
 フラッシュメッセージを利用しないのであればconnect-flashは不要。


まずは動作確認。
http://xxxxx/ にアクセスするとログイン画面が表示される。
そこで 初期設定されている ID/Password を入力するとマイページに遷移できる。
マイページには設定画面へのリンクとログアウトのリンクがある。

それでは処理の流れを説明する。
ログインボタンを押すと以下が走る。

router.post(
	'/login', 
	passport.authenticate( 'local', { successRedirect: '/mypage', failureRedirect: '/', failureFlash: true }), 
	function(req, res, next) {
		//認証成功した場合のコールバック
		//成功時のリダイレクトは successRedirect で指定しているので、ここですることは特にないかもしれない。
	}
);

ここの passport.authenticate() によって、app.js の 以下の処理が走る。
function の username, password にはフォームで入力した値が入ってくる。
今回はハードコーディングした値(myname, mypassword)でログイン成功とする。

passport.use(
	new LocalStrategy(
		{
			//Viewのアカウント入力フォームのname属性を指定する
			usernameField: 'user_name', 
			passwordField: 'user_password'
		},
		function(username, password, done) {
			//routerのpassport.authenticate()が呼ばれたらここの処理が走る。

			if(username == 'myname' && password == 'mypassword'){
				//ログイン成功
				//今回はここの判定式をハードコーディングにする
        		return done(null, username);
			}

			//ログイン失敗
			//messageはログイン失敗時のフラッシュメッセージ。
			//各routerの req.flash() で取得できる。
       		return done(null, false, {message:'ID or Passwordが間違っています。'});
		}
	)
);

ここでのポイントは done() 。
done() の第2引数にオブジェクトを指定することで、
ログイン成功と判断し、指定されたオブジェクトをシリアライズしてセッションに保存する。
その際のシリアライズ処理をフックするのが passport.serializeUser() になる。
今回は username を保存しているだけ。

//認証した際のオブジェクトをシリアライズしてセッションに保存する。
passport.serializeUser(function(username, done) {
	console.log('serializeUser');
	done(null, username);
});

成功した場合は passport.authenticate() 直後のfunction()が呼ばれる。

router.post(
	'/login', 
	passport.authenticate( 'local', { successRedirect: '/mypage', failureRedirect: '/', failureFlash: true }), 
	function(req, res, next) {
		//認証成功した場合のコールバック
		//成功時のリダイレクトは successRedirect で指定しているので、ここですることは特にないかもしれない。
	}
);

done()の第2引数がfalseだと失敗とみなされる。
失敗の場合は failureRedirect で指定したパスに飛ばされる。

これで認証処理は終了。

次にマイページの処理を確認してみる。

router.get('/mypage', function(req, res, next) {

	//log
	console.log('mypage');
	console.log(req.user);

	if(req.user){
		//userが存在する場合は認証済み
		res.render('mypage', { title: 'Mypage' });
	}else{
		res.redirect('/');
	}
});

マイページはログインしたユーザーしか参照できないので、 req.user による判定を実装している。
req.user が存在する場合は認証済みで、存在しない場合は認証していない。

req.user には passport.deserializeUser() でデシリアライズした値が入る。
passport.deserializeUser() はセッション情報をデシリアライズする際に呼ばれる。
以下のように msg という情報を追加することで、 req.user にも msg が渡される。
これを利用するとセッションには user_id を保存しておいて、 deserializeUser() でそのidを利用してDBからユーザーのプロフィール情報を引っ張って、各routerに渡す事が可能になる。

passport.deserializeUser(function(username, done) {
	console.log('deserializeUser');
	done(null, {name:username, msg:'my message'});
});

あとはログアウト処理だけ。

//ログアウト
router.get('/logout', function(req, res, next) {
	req.logout();
	res.redirect('/');
});

passportの挙動についての説明は以上で終了。

認証にDBを利用する場合は
ハードコーディングしていたID/Password部分を
DBから参照するように修正すればいい。
ここは自分でやってみよう。