9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Node.js Express】Passport.jsを用いて認証機能を実装する。(passportLocalMongooseをプラグイン)

Posted at

※当方駆け出しエンジニアのため、間違っていることも多々あると思いますので、ご了承ください。また、間違いに気付いた方はご一報いただけると幸いです。

###要件

  • pasport.jsを用いたパスワード認証を作成する。
  • 認証ストラテジにはpassportLocalMongooseをプラグインで使用。
  • 入力する値はname email password
  • 認証(ログイン、サインアップ)以外のページはexpressデフォルトのホームページ("/")のみ。(そのページに認証をかける。)

###アプリケーションに必要なパッケージをインストールする。

前提

・ express-generatorで雛形を作成済。
・ mongooseを用いてmongodbと接続完了。
・ package.json↓

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レイアウトレンダリングをロードし、共通の土台レイアウトを作成する。

app.js
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>

##ログイン画面

image.png

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>

##サインアップ画面
image.png

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を表示させる。

##とりあえずのルーティング(ログイン、サインアップ)を作成する。

app.js
const usersRouter = require('./routes/users');
//分離ファイルの読み込み

app.use('/users', usersRouter);
// '/users'タッチ時のミドルウェアに分離ファイルを指定
routes/users.js
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オブジェクトをエクスポート

##とりあえずのコントーラー(ログイン、サインアップ)を作成する。

controllers/usersController.js

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;

##とりあえずのモデルを作成する。

m_user.js
"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)

##サインアップから保存処理。

usersController.js
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で必要なミドルウェア設定を行う。

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プラグインを設定する。

m_user.js
"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メソッドを追記

usersController.js
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;

##サインアップから保存確認

image.png
パスワードの代わりに、saltとhashが保存されている。

##ログイン時に認証を実装する。

コントローラーに、authenticateメソッドを追加する。

const passport = require("passport");
//passportのロード

  authenticate: passport.authenticate("local", {
  //authenticateメソッドがstrategyから自動的に認証を確認。
    failureRedirect: "/users/login",  //成功時の遷移先
    successRedirect: "/",            //失敗時の遷移先
  }),

ルーティングのlogin入力ポスト時のルーティングを設定。

routes/users.js
router.post("/users/login", usersController.authenticate);

ユーザーの状態を保存するミドルウェアを組み込み。

app.js
app.use((req, res, next) => {
  res.locals.loggedIn = req.isAuthenticated();//ユーザーのログイン状態を req.localsに格納
  res.locals.currentUser = req.user;//ログインユーザー情報を格納
  next();
});

##ログイン状態で名前を表示させる。(併せてログアウト遷移ボタンを追加)

views/layout.ejs
  <header>
    <% if (loggedIn) { %>
      <p>名前:<%= currentUser.name %></p>
      <a href="/users/logout">Log out</a>
      <% } %>
  </header>

##ルーティングにロウグアウトの遷移を追記

routes/users.js
router.get('/logout', usersController.logout);

##ログアウト時の挙動をコントローラーに追記

controllers/usersController.js
  logout: (req, res, next) => {
    req.logout();
    res.redirect("/users/login");
  },

##ログイン状態チェックのコントローラーを追記

controllers/usersController.js
  loginCheck: (req, res, next) => {
    if (!req.isAuthenticated()) {
      res.redirect("/users/login");
    }
    next();
  }

ホームページに認証チェックとしてミドルウェアを埋め込む

routes/index.js
route.get('/', usersController.loginCheck, function (req, res, next) {
  res.render('index', { title: 'Express' });
});

参考:

passport-local-mongoose - npm
Passport | 認証

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?