
今回利用する主なモジュールのバージョンは以下の通りです。
node v0.12.2
express v4.12.1
mongod v2.4.9
passport v0.2.1
passport-local v.1.0.0
passport-local-mongoose v.1.0.0
今回のサンプルアプリではデータベースに MongoDB を利用します。MongoDB のインストールが未だでしたらこちらを参考にMongoDBをインストールしておいてください。
Expressプロジェクトの雛形を作成
Express4なプロジェクトの雛形を作るためにまずは express-generator をインストールして
$ sudo npm install -g express-generator
雛形生成を実行します。プロジェクト名は passport-express4-example としました。
$ express passport-express4-example
生成されたExpress4プロジェクト構成はこのようになります。
$ tree passport-express4-example/
passport-express4-example/
├── app.js
├── bin
│ └── www
├── package.json
├── public
│ ├── images
│ ├── javascripts
│ └── stylesheets
│ └── style.css
├── routes
│ ├── index.js
│ └── users.js
└── views
├── error.jade
├── index.jade
└── layout.jade
依存関係を追加
package.json に dependencies (依存ライブラリ)を追加しましょう。太字は追加した箇所です。
{
"name": "passport-express4-example",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www"
},
"dependencies": {
"body-parser": "~1.12.0",。。。。。
"cookie-parser": "~1.3.4",
"debug": "~2.1.1",
"express": "~4.12.2",
"jade": "~1.9.2",
"morgan": "~1.5.1",
"serve-favicon": "~2.2.0",
"express-session": "^1.10.1",
"mongoose": "^4.0.2",
"passport": "^0.2.1",
"passport-local": "^1.0.0",
"passport-local-mongoose": "^1.0.0",
"should": "~2.1.0",
"chai": "~1.8.1",
"mocha": "~1.14.0"
}
}npm install で依存モジュールをインストールします。
$ cd passport-express4-example $ sudo npm install
さて、ここで一旦サンプルアプリが動くことを確認しておきましょう。
$ npm start
http://localhost:3000/ にアクセスしてWelcomeページが表示されているか確認してください。
ちゃんと動いていることが確認できたら Ctrl+C でサンプルアプリを強制終了して次に進みます。
MongoDBの起動
次に MongoDB を起動します。
ここでは datapath と logpath を指定していますが、ここは皆さんの環境に合わせて書き換えてください。
$ sudo mongod --dbpath ~/tmp/mongodb/data/ --logpath ~/tmp/mongodb/mongodb.log &
app.js
では app.js を編集しましょう。こちらも太字が追加した箇所です。
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var routes = require('./routes/index');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(require('express-session')({
secret: 'secret secret',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', routes);
// passport config
var Account = require('./models/account');
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
// mongoose
mongoose.connect('mongodb://localhost/passport_local_mongoose_express4');
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handlers
// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
module.exports = app;
modelsを作成
次にプロジェクトルート直下に models フォルダを作り、account.js を新規作成します。これは MongoDB に格納されるユーザアカウント情報のモデルです。
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var Account = new Schema({
username: String,
password: String
});
Account.plugin(passportLocalMongoose);
module.exports = mongoose.model('Account', Account);routesを作成
次にプロジェクトルート直下の routes フォルダの index.js を以下のように編集します。今回はルーティング情報を index.js に集約させることにしましょう。
var express = require('express');
var passport = require('passport');
var Account = require('../models/account');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
console.log("req.user", req.user);
res.render('index', {
user: req.user
});
});
/* GET register page */
router.get('/register', function(req, res) {
res.render('register', {});
});
/* Register a user account */
router.post('/register', function(req, res) {
Account.register(new Account({
username: req.body.username
}), req.body.password, function(err, account) {
console.log("req.body.username", req.body.username);
if (err) {
console.log("err", err);
return res.render("register", {
errInfo: "Sorry. That username already exists. Try again."
});
}
passport.authenticate('local')(req, res, function() {
res.redirect('/');
});
});
});
/* GET login page */
router.get('/login', function(req, res) {
res.render('login', {
user: req.user,
errInfo: req.query.err
});
});
/* Try to login */
router.post('/login', passport.authenticate('local', {
failureRedirect: "/login?err=unauthorized"
}), function(req, res) {
res.redirect('/');
});
/* Do logout */
router.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
/* GET member-only page */
router.get('/member', function(req, res) {
if (!req.isAuthenticated()) res.redirect("/login");
res.render('member', {
user: req.user
});
});
/* For checking if webserver will be dead or alive */
router.get('/ping', function(req, res) {
res.status(200).send("pong!");
});
module.exports = router;
ここまできたらもう一度アプリを立ち上げてエラーが無いか確認しておきましょう。
$ npm start
問題なくアプリが立ち上がったら http://localhost:3000/ping を叩いてみてください。/ping はアプリの生存確認のために作ったAPIです。画面に pong! が返ってくればアプリは正常に動いています。
viewsを作成
次に画面を編集 or 新規作成します。画面を定義する jade ファイルが views フォルダ配下にあります。
layout.jade (全画面共通レイアウト)
doctype html
html
head
title= title
meta(name='viewport', content='width=device-width, initial-scale=1.0')
link(href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css', rel='stylesheet', media='screen')
link(rel='stylesheet', href='/stylesheets/style.css')
body
block content
script(src='http://code.jquery.com/jquery.js')
script(src='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js')index.jade (トップページ)
extends layout
block content
if (!user)
a(href="/login") Login
br
a(href="/register") Register
if (user)
p You are currently logged in as #{user.username}
a(href="/logout") Logoutlogin.jade (ログインページ)
extends layout
block content
.container
h1 Login Page
p.lead Say something worthwhile here.
br
form(role='form', action="/login",method="post", style='max-width: 300px;')
.form-group
input.form-control(type='text', name="username", placeholder='Enter Username')
.form-group
input.form-control(type='password', name="password", placeholder='Password')
button.btn.btn-default(type='submit') Submit
a(href='/')
button.btn.btn-primary(type="button") Cancelregister.jade (会員登録ページ)
extends layout
block content
.container
h1 Register Page
p.lead Say something worthwhile here.
br
form(role='form', action="/register",method="post", style='max-width: 300px;')
.form-group
input.form-control(type='text', name="username", placeholder='Enter Username')
.form-group
input.form-control(type='password', name="password", placeholder='Password')
button.btn.btn-default(type='submit') Submit
a(href='/')
button.btn.btn-primary(type="button") Cancel
br
h4(style="color:red")= errInfomember.jade (会員専用ページ)
extends layout
block content
if(user)
p Hi #{user.username}. This page is member-only.
a(href="/logout") Logoutexpress4 + passport な会員サイトのサンプルアプリは以上で出来上がりです。
出来上がったアプリの機能一覧は以下のとおりとなります。
トップページ
http://localhost:3000/
会員登録ページ
http://localhost:3000/register
ログインページ
http://localhost:3000/login
会員専用ページ
http://localhost:3000/member
ログアウト
http://localhost:3000/logout
Unit Test
アプリができたら念の為Unitテストもしておきましょう。
初めに package.json の dependencies に追加した mocha, chai, should はUnitテストのためのモジュールです。
プロジェクトルートに test フォルダを新規作成し、 test.user.js を新規作成します。
var should = require("should");
var mongoose = require('mongoose');
var Account = require("../models/account.js");
var db;
describe('Account', function() {
before(function(done) {
db = mongoose.connect('mongodb://localhost/test');
done();
});
after(function(done) {
mongoose.connection.close();
done();
});
beforeEach(function(done) {
var account = new Account({
username: '12345',
password: 'testy'
});
account.save(function(error) {
if (error) console.log('error' + error.message);
else console.log('no error');
done();
});
});
it('find a user by username', function(done) {
Account.findOne({
username: '12345'
}, function(err, account) {
account.username.should.eql('12345');
console.log(" username: ", account.username);
done();
});
});
afterEach(function(done) {
Account.remove({}, function() {
done();
});
});
});上記の test.user.js ではユーザアカウントの新規登録・取得・削除までをひと通りテストします。
以下のコマンドでUnitテストを実行することができます。
$ node_modules/mocha/bin/mocha test/ test.user.js
child_process: customFds option is deprecated, use stdio instead.
no error
username: 12345
․
1 passing (222ms)エラーは検出されませんでしたので、これにてUnitテストも無事終了です。
■参考にさせていただいたサイト
User Authentication with Passport and Express 4 - Michael Herman
■関連記事
Express4+PassportでGoogle OAuth認証の簡単なサンプル
0 件のコメント:
コメントを投稿