「入門Node.jsプログラミング 」の感想・備忘録9

スポンサーリンク
「入門Node.jsプログラミング 」の感想・備忘録8の続き

Lesson25

認証処理のまとめ

  • npm install express ejs express-ejs-layouts connect-flash cookie-parser express-session express-validator passport passport-local-mongoose method-override
"use strict";

const express = require("express"),
  layouts = require("express-ejs-layouts"),
  app = express(),
  router = express.Router(),
  usersController = require("./controllers/usersController.js"),
  mongoose = require("mongoose"),
  methodOverride = require("method-override"),
  passport = require("passport"),
  cookieParser = require("cookie-parser"),
  expressSession = require("express-session"),
  connectFlash = require("connect-flash"),
  User = require("./models/user");

mongoose.connect(
  "mongodb://localhost:27017/lesson25",
  { useNewUrlParser: true }
);

app.set("port", process.env.PORT || 3000);
app.set("view engine", "ejs");

router.use(
  methodOverride("_method", {
    methods: ["POST", "GET"]
  })
);

router.use(layouts);
router.use(express.static("public"));
router.use(
  express.urlencoded({
    extended: false
  })
);
router.use(express.json());

router.use(cookieParser("secretLesson25"));
router.use(
  expressSession({
    secret: "secretLesson25",
    cookie: {
      maxAge: 4000000
    },
    resave: false,
    saveUninitialized: false
  })
);
router.use(connectFlash());

router.use(passport.initialize());
router.use(passport.session());
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

router.use((req, res, next) => {
  res.locals.loggedIn = req.isAuthenticated();
  res.locals.currentUser = req.user;
  res.locals.flashMessages = req.flash();
  next();
});
router.get("/", (req, res) => {
  res.render("index");
});
router.get("/users", usersController.index, usersController.indexView);
router.get("/users/new", usersController.new);
router.post(
  "/users/create",
  usersController.validate,
  usersController.create,
  usersController.redirectView
);
router.get("/users/login", usersController.login);
router.post("/users/login", usersController.authenticate);
router.get("/users/logout", usersController.logout, usersController.redirectView);

app.use("/", router);

app.listen(app.get("port"), () => {
  console.log(`Server running at http://localhost:${app.get("port")}`);
});
<div class="data-form">
  <form class="form-signin" action="/users/create" method="POST">
    <h2 class="form-signin-heading">Create a new user:</h2>
    <label for="name">Name</label>
    <input type="text" name="name" id="name" class="form-control" placeholder="name" autofocus>
    <label for="inputPassword">Password</label>
    <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
    <label for="inputEmail">Email address</label>
    <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required>
    <label for="inputZipCode">Zip Code</label>
    <input type="text" name="zipCode" id="inputZipCode" pattern="[0-9]{5}" class="form-control" placeholder="Zip Code" required>
    <br /><button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
  </form>
</div>
<form class="form-signin" action="/users/login" method="POST">
  <h2 class="form-signin-heading">Login:</h2>
  <label for="inputEmail" class="sr-only">Email</label>
  <input type="text" name="email" id="inputEmail" class="form-control" placeholder="Email" autofocus required>
  <label for="inputPassword" class="sr-only">Password</label>
  <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required>
  <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
</form>
<h2 class="center">Users Table</h2>
<div class="center">
  <a class="button" href="/users/new">Create User</a>
</div>
<table class="table">
  <thead>
    <tr>
      <th>Name</th>
      <th>Email</th>
      <th>Zip Code</th>
    </tr>
  </thead>
  <tbody>
    <% users.forEach(user => { %>
    <tr>
      <td>
        <%= user.name %>
      </td>
      <td>
        <%= user.email %>
      </td>
      <td>
        <%= user.zipCode %>
      </td>
    </tr>
    <% }); %>
  </tbody>
</table>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</head>
<body>
<div class="login">
<a href="/">TOP</a>
<% if (loggedIn) { %>
<p>Logged in as
<a href="<%=`/users/${currentUser._id}`%>">
<%= currentUser.name %></a>
<span class="log-out">
<a href="/users/logout">Log out</a>
</span>
</p>
<%} else {%>
<a href="/users/login">Log In</a>
<% } %>
</div>
<div class="flashes">
<% if (flashMessages) { %>
<% if (flashMessages.success) { %>
<div class="flash success">
<%= flashMessages.success %>
</div>
<% } else if (flashMessages.error) { %>
<div class="flash error">
<%= flashMessages.error %>
</div>
<% } %>
<% } %>
</div>
<%- body %>
</body>
</html>
<div class="col-sm-6">
  <dl>
    <li><a href="/users">/users</a></li>
    <li><a href="/users/new">/users/new</a></li>
  </dl>
</div>
"use strict";

const User = require("../models/user"),
  passport = require("passport"),
  { body, validationResult } = require('express-validator'),
  getUserParams = body => {
    return {
      name: body.name,
      email: body.email,
      password: body.password,
      zipCode: body.zipCode
    };
  };

module.exports = {
  index: (req, res, next) => {
    User.find()
      .then(users => {
        res.locals.users = users;
        next();
      })
      .catch(error => {
        console.log(`Error fetching users: ${error.message}`);
        next(error);
      });
  },
  indexView: (req, res) => {
    res.render("users/index");
  },

  new: (req, res) => {
    res.render("users/new");
  },

  create: (req, res, next) => {
    if (req.skip) return next();
    let newUser = new User(getUserParams(req.body));
    User.register(newUser, req.body.password, (e, user) => {
      if (user) {
        req.flash("success", `${user.name}'s account created successfully!`);
        res.locals.redirect = "/users";
        next();
      } else {
        req.flash("error", `Failed to create user account because: ${e.message}.`);
        res.locals.redirect = "/users/new";
        next();
      }
    });
  },

  redirectView: (req, res, next) => {
    let redirectPath = res.locals.redirect;
    if (redirectPath !== undefined) res.redirect(redirectPath);
    else next();
  },
  
  login: (req, res) => {
    res.render("users/login");
  },
  
  validate: (
    body("email").trim().isEmail().notEmpty(),
    body("zipCode").trim().isInt().isLength({
      min: 5,
      max: 5
    }).notEmpty(),
    body("password").trim().notEmpty(),
    (req, res, next) => {
      const error = validationResult(req);
      if (!error.isEmpty()) {
        let messages = error.array().map(e => e.msg);
        req.skip = true;
        req.flash("error", messages.join(" and "));
        res.locals.redirect = "/users/new";
        next();
      } else {
        next();
      }
    }
  ),
  
  authenticate: passport.authenticate("local", {
    failureRedirect: "/users/login",
    failureFlash: "Failed to login.",
    successRedirect: "/",
  }),
  
  logout: (req, res, next) => {
    req.logout(()=>{
      req.flash("success", "You have been logged out!");
      res.locals.redirect = "/";
      next();
    });
  }
};
"use strict";

const mongoose = require("mongoose"),
  { Schema } = require("mongoose"),
  passportLocalMongoose = require("passport-local-mongoose");

const userSchema = new Schema(
  {
    name: {
      type: String,
      trim: true
    },
    email: {
      type: String,
      required: true,
      lowercase: true,
      unique: true
    },
    zipCode: {
      type: Number,
      min: [1000, "Zip code too short"],
      max: 99999
    },
    password: {
      type: String,
      required: true
    },
  },
  {
    timestamps: true
  }
);

userSchema.plugin(passportLocalMongoose, {
  usernameField: "email"
});

module.exports = mongoose.model("User", userSchema);

コメント