※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。
###要件
- pasport.jsを用いたパスワード認証を作成する。
- 認証ストラテジにはpassportLocalMongooseをプラグインで使用。
- 入力する値はname email password
- 認証(ログイン、サインアップ)以外のページはexpressデフォルトのホームページ("/")のみ。(そのページに認証をかける。)
###アプリケーションに必要なパッケージをインストールする。
前提
・ express-generatorで雛形を作成済。
・ mongooseを用いてmongodbと接続完了。
・ package.json↓
{
"name": "passport_app",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "nodemon ./bin/www"
},
"dependencies": {
"body-parser": "^1.19.0",
"cookie-parser": "~1.4.4", //依存
"express-session": "^1.17.1", //依存
"mongoose": "^5.11.9", //依存
"passport": "^0.4.1", //本体
"passport-local-mongoose": "^6.0.1", //本体
"ejs": "^3.1.5",
"express": "~4.16.1",
"express-ejs-layouts": "^2.5.0",
}
}
前提を構築するまでの手順は、下記の記事を参考にしてください。
[【アプリ開発 1】【Node.js express Docker】 Dockerを用いてNode.js Express MongoDB(mongoose)の環境を構築する【2020年12月】]
(https://qiita.com/sho_U/items/43f6483aac8ca45a12f6)
#一旦サインアップからユーザーを登録できるまで作成する。
##とりあえずのビューを(共通、ログイン、サインアップ)作成する。
EJSレイアウトレンダリングをロードし、共通の土台レイアウトを作成する。
const layouts = require("express-ejs-layouts");//モジュールロード
app.use(layouts);//ミドルウェアとして組み込む。
views/layout.ejs を作成する。
ヘッダーとメインコンテンツだけシンプルな構成
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<header>
<p>名前:</p>
</header>
<div class="main">
<%- body %>
</div>
</body>
</html>
##ログイン画面
views/users/login.ejs
<form action="/users/login" method="POST">
<label>Email</label>
<input type="text" name="email" autofocus required>
<label>Password</label>
<input type="password" name="password" required>
<button type="submit">Login</button>
</form>
view/users/signup.ejs
<form action="/users/create" method="POST">
<label>name</label>
<input type="text" name="name" autofocus required>
<label>Email</label>
<input type="text" name="email" autofocus required>
<label>Password</label>
<input type="password" name="password" required>
<button type="submit">Singup</button>
</form>
express-ejs-layoutsにより、ヘッダーを設定。後ほど名前には、nameを表示させる。
##とりあえずのルーティング(ログイン、サインアップ)を作成する。
const usersRouter = require('./routes/users');
//分離ファイルの読み込み
app.use('/users', usersRouter);
// '/users'タッチ時のミドルウェアに分離ファイルを指定
const express = require('express');
const router = express.Router();
const usersController = require('../controllers/usersController');
router.get('/login', usersController.login);
router.get('/new', usersController.new);
router.post('/create', usersController.create);
//routerに各メソッドと、遷移先を組み込み。
module.exports = router; //routerオブジェクトをエクスポート
##とりあえずのコントーラー(ログイン、サインアップ)を作成する。
const usersController = {
login: (req, res) => {
res.render("../views/users/login", { layout: false });
//ログイン画面にはヘッダーは不要なので、layoutを表示させない様にしている。
},
new: (req, res) => {
res.render("../views/users/signup", { layout: false });
//サインアップ画面にはヘッダーは不要なので、layoutを表示させない様にしている。
},
create: (req, res) => {
res.render("/");
}
}
module.exports = usersController;
##とりあえずのモデルを作成する。
"use strict";
const mongoose = require("mongoose")
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true, //必須
unique: true //ユニーク
},
email: {
type: String,
required: true, //必須
unique: true //ユニーク
},
password: { //nameのバリューにオブジェクトで渡す。
type: String,
min: [4, "Too short"]//最小値エラーメッセージ を設定
},
});
module.exports = mongoose.model("User", userSchema)
##サインアップから保存処理。
const User = require("../models/m_user");
const usersController = {
//(略)
new: (req, res) => {
res.render("../views/users/signup", { layout: false });
},
create: (req, res) => {
User.create({
name: req.body.name,
email: req.body.email,
password: req.body.password
},
(error, result) => {
if (error) { res.redirect("/users/new") }
}
)
}
}
module.exports = usersController;
###ここまでの実装
サインアップ画面から、name、email、passwordをMongoDBに保存できる状態。
##Passportを導入する。
app.jsで必要なミドルウェア設定を行う。
const passport = require("passport")
const cookieParser = require("cookie-parser");
const expressSession = require("express-session");
//必要なパッケージをロードする。
const User = require("./models/m_user");
//Userモデルをロード
app.use(cookieParser("secret"));
//クッキーの秘密鍵の設定
app.use(
expressSession({
secret: "secret",
cookie: {
maxAge: 4000000
},
resave: false,
saveUninitialized: false
})
);
//セッションの設定
app.use(passport.initialize());
//passportを初期化ミドルウェアを組み込む
app.use(passport.session());
//passportに対しセッションを使用するよう指定
app.use(testUser.createStrategy());
//デフォルトのログインストラテジーを設定する。
app.serializeUser(User.serializeUser());
app.deserializeUser(testUser.deserializeUser());
//passportを、ユーザーデータの圧縮・暗号化/復号を行うように設定
Userモデルにpassport-local-mongooseプラグインを設定する。
"use strict";
const mongoose = require("mongoose");
const passportLocalMongoose = require("passport-local-mongoose");
//passport-local-mongooseをモジュールロード
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true, //必須
unique: true //ユニーク
},
email: {
type: String,
required: true, //必須
unique: true //ユニーク
},
//passwordパラメータを削除(passportが自動的にハッシュとソルトのパラメーターを追加してくれる)
});
userSchema.plugin(passportLocalMongoose, {//userSchemaにpassportLocalMongooseをプラグイン
usernameField: "email"
});//ログイン認証に使うパラメーターを指定(デフォルトのusernameから変更)
module.exports = mongoose.model("User", userSchema)
##コントローラーのcreateアクションにregisterメソッドを追記
const User = require("../models/m_user");
const usersController = {
login: (req, res) => {
res.render("../views/users/login", { layout: false });
},
new: (req, res) => {
res.render("../views/users/signup", { layout: false });
},
create: (req, res, next) => {
if (req.skip) next();
const newTestUser = {
name: req.body.name,
email: req.body.email,
};
User.register(newTestUser, req.body.password, (error, user) => {
//registerメソッドはpassportLocalMongooseに組み込まれているメソッド。
//第一引数に、認証以外に保存するパラメータ、第二引数に認証に用いるパラメータ(これがsaltとhash化される。)
if (user) {
console.log("success")
next();
} else {
console.log(error.message);
next();
}
})
}
}
module.exports = usersController;
##サインアップから保存確認
##ログイン時に認証を実装する。
コントローラーに、authenticateメソッドを追加する。
const passport = require("passport");
//passportのロード
authenticate: passport.authenticate("local", {
//authenticateメソッドがstrategyから自動的に認証を確認。
failureRedirect: "/users/login", //成功時の遷移先
successRedirect: "/", //失敗時の遷移先
}),
ルーティングのlogin入力ポスト時のルーティングを設定。
router.post("/users/login", usersController.authenticate);
ユーザーの状態を保存するミドルウェアを組み込み。
app.use((req, res, next) => {
res.locals.loggedIn = req.isAuthenticated();//ユーザーのログイン状態を req.localsに格納
res.locals.currentUser = req.user;//ログインユーザー情報を格納
next();
});
##ログイン状態で名前を表示させる。(併せてログアウト遷移ボタンを追加)
<header>
<% if (loggedIn) { %>
<p>名前:<%= currentUser.name %></p>
<a href="/users/logout">Log out</a>
<% } %>
</header>
##ルーティングにロウグアウトの遷移を追記
router.get('/logout', usersController.logout);
##ログアウト時の挙動をコントローラーに追記
logout: (req, res, next) => {
req.logout();
res.redirect("/users/login");
},
##ログイン状態チェックのコントローラーを追記
loginCheck: (req, res, next) => {
if (!req.isAuthenticated()) {
res.redirect("/users/login");
}
next();
}
ホームページに認証チェックとしてミドルウェアを埋め込む
route.get('/', usersController.loginCheck, function (req, res, next) {
res.render('index', { title: 'Express' });
});
参考: