๐Ÿ˜Ž ๊ณต๋ถ€ํ•˜๋Š” ์ง•์ง•์•ŒํŒŒ์นด๋Š” ์ฒ˜์Œ์ด์ง€?

[Login & Register authentication with Node js] (9) ์ตœ์ข… ์ฝ”๋“œ์•ˆ ๋ณธ๋ฌธ

๐Ÿ‘ฉ‍๐Ÿ’ป ๋ฐฑ์—”๋“œ(Back-End)/Node js

[Login & Register authentication with Node js] (9) ์ตœ์ข… ์ฝ”๋“œ์•ˆ

์ง•์ง•์•ŒํŒŒ์นด 2023. 3. 20. 00:58
728x90
๋ฐ˜์‘ํ˜•

<๋ณธ ๋ธ”๋กœ๊ทธ๋Š” Traversy Media ์˜ ์œ ํŠœ๋ธŒ๋ฅผ ์ฐธ๊ณ ํ•ด์„œ ๊ณต๋ถ€ํ•˜๋ฉฐ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค :-)>

=> Node.js With Passport Authentication | Full Project

=> authentication app with login, register and access control using Node.js, Express, Passport, Mongoose

 

 

 

๐Ÿฅ• passport๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋กœ๊ทธ์ธ, ํšŒ์›๊ฐ€์ž… ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•˜๊ณ  auth ์ธ์ฆ๊นŒ์ง€ ์™„๋ฃŒํ•˜๊ธฐ

์ฐธ๊ณ  : https://github.com/gani0325/2023/tree/main/Web/PassportAuthentication

 

๐Ÿง middlewares/auth.js

module.exports = {
  ensureAuthenticated: (req, res, next) => {
    if (req.isAuthenticated()) {
      return next();
    }
    req.flash("error_msg", "Plz, log in to view this resource!");
    res.redirect("/users/login");
  }
}

 

๐Ÿง middlewares/passport.js

// ์ธ์ฆ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋Š” Node.js์˜ ์ธ์ฆ ๋ฏธ๋“ค์›จ์–ด

const LocalStrategy = require("passport-local").Strategy;
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");

// Load user model
const User = require("../models/User");

module.exports = (passport) => {
  passport.use(
    new LocalStrategy({ usernameField: "email" }, (email, password, done) => {
      // Match User
      User.findOne({ email: email })
        .then(user => {
          // user ๊ฐ€ ์—†๋‹ค๋ฉด done
          if (!user) {
            return done(null, false, { message: "That email is not registered!" });
          }
          // Match password (๊ธฐ์กด ๋น„๋ฐ€๋ฒˆํ˜ธ์™€ ์ž…๋ ฅํ•œ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ฒดํฌ)
          bcrypt.compare(password, user.password, (err, isMatch) => {
            if (err) throw err;

            if (isMatch) {
              return done(null, user);
            } else {
              return done(null, false, { message: "Email or Password is incorrect!" });
            }
          });
        })
        .catch(err => console.log(err));
    })
  );

  // login์ด ์ตœ์ดˆ๋กœ ์„ฑ๊ณตํ–ˆ์„ ๋•Œ๋งŒ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
  // done(null, user.id)๋กœ ์„ธ์…˜์„ ์ดˆ๊ธฐํ™”
  passport.serializeUser(function (user, cb) {
    process.nextTick(function () {
      return cb(null, user);
    });
  });

  // ์‚ฌ์šฉ์ž๊ฐ€ ํŽ˜์ด์ง€๋ฅผ ๋ฐฉ๋ฌธํ•  ๋•Œ๋งˆ๋‹ค ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
  // done(null, id)๋กœ ์‚ฌ์šฉ์ž์˜ ์ •๋ณด๋ฅผ ๊ฐ request์˜ user ๋ณ€์ˆ˜์— ๋„ฃ์Œ  
  passport.deserializeUser(function (user, cb) {
    process.nextTick(function () {
      return cb(null, user);
    });
  });
}

 

๐Ÿง models/User.js

const mongoose = require("mongoose");

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true
  },
  password: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now
  }
}, { collection: 'Passport' });

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

 

๐Ÿง public/images/image.jpg

 

๐Ÿง routes/index.js

const express = require("express");
const router = express.Router();
const { ensureAuthenticated } = require("../middlewares/auth");

// Main page
router.get("/", (req, res) => {
  // rednering ํ•  ๋•Œ view์˜ ํŒŒ์ผ ์ด๋ฆ„ ์จ์•ผ๋จ
  res.render("welcome");
});

// dashboard
router.get("/dashboard", ensureAuthenticated, (req, res) => {
  // rendering ํ•  ๋•Œ view์˜ ํŒŒ์ผ ์ด๋ฆ„ ์จ์•ผ๋จ
  res.render("dashboard", {
    name : req.user.name
  });
});

module.exports = router;

 

๐Ÿง routes/user.js

const express = require("express");
const router = express.Router();
const bcrypt = require("bcryptjs");
const passport = require("passport");
// user model
const User = require("../models/User");

// Login Page
router.get("/login", (req, res) => {
  res.render("login");
});

// Register Page
router.get("/register", (req, res) => {
  res.render("register");
});

// Register Handle
router.post('/register', (req, res) => {
  const { name, email, password, password2 } = req.body;
  let errors = [];

  if (!name || !email || !password || !password2) {
    errors.push({ msg: 'Please enter all fields' });
  }

  if (password != password2) {
    errors.push({ msg: 'Passwords do not match' });
  }

  if (password.length < 6) {
    errors.push({ msg: 'Password must be at least 6 characters' });
  }

  if (errors.length > 0) {
    res.render('register', {
      errors,
      name,
      email,
      password,
      password2
    });
  } else {
    // validation pass
    User.findOne({ email: email })
      .then(user => {
        // email์„ ์ฐพ์•˜๋Š”๋ฐ ๋งŒ์•ฝ์— user๊ฐ€ ์žˆ๋‹ค๋ฉด
        if (user) {
          errors.push({ msg: "Email is already registered!" });
          res.render('register', {
            errors,
            name,
            email,
            password,
            password2
          });
        } else {
          // ํšŒ์›๊ฐ€์ž…๋œ email์ด ์—†๋‹ค๋ฉด
          const newUser = new User({
            name,
            email,
            password
          });

          console.log(newUser);

          // hash password
          bcrypt.genSalt(10, (err, salt) =>
            bcrypt.hash(newUser.password, salt, (err, hash) => {
              if (err) throw err;
              // Set password to hased
              newUser.password = hash;
              // save user
              newUser.save()
                .then(user => {
                  req.flash("success_msg", "You are now registered and can log in!!");
                  res.redirect('/users/login');
                })
                .catch(err => console.log(err));
            }))
        }
      });
  }
});

// Login handle
router.post("/login", (req, res, next) => {
  passport.authenticate("local", {
    // ์„ฑ๊ณตํ•˜๋ฉด ๋ฉ”์ธ์œผ๋กœ
    successRedirect : "/dashboard",
    // ์‹คํŒจํ•˜๋ฉด ๋‹ค์‹œ ๋กœ๊ทธ์ธ
    failureRedirect : "/users/login",
    failureFlash : true
  }) (req, res, next);
});

// Logout handle
router.get('/logout', (req, res, next) => {
  req.logOut(err => {
    if (err) {
      return next(err);
    } else {
      console.log('๋กœ๊ทธ์•„์›ƒ๋จ.');
      req.flash("success-msg", "You are logged out!");
      res.redirect("/users/login");
    }
  });
});

module.exports = router;

 

๐Ÿง views/partials/messages.ejs

<% if(typeof errors !="undefined" ) { %>
  <% errors.forEach(function(error) { %>
    <div class="alert alert-warning alert-dismissible fade show" role="alert">
      <%= error.msg %>
      <button type="button" class="close" data-bs-dismiss="alert" aria-label="Close"></button>
      <span aria-hidden="true">&times;</span>
    </div>
  <% }); %>
<% } %>

<% if(success_msg != "") { %>
  <div class="alert alert-success alert-dismissible fade show" role="alert">
    <%= success_msg %>
    <button type="button" class="close" data-bs-dismiss="alert" aria-label="Close"></button>
    <span aria-hidden="true">&times;</span>
  </div>
<% } %>

<% if(error_msg != "") { %>
  <div class="alert alert-warning alert-dismissible fade show" role="alert">
    <%= error_msg %>
    <button type="button" class="close" data-bs-dismiss="alert" aria-label="Close"></button>
    <span aria-hidden="true">&times;</span>
  </div>
<% } %>

<% if(error != "") { %>
  <div class="alert alert-warning alert-dismissible fade show" role="alert">
    <%= error %>
    <button type="button" class="close" data-bs-dismiss="alert" aria-label="Close"></button>
    <span aria-hidden="true">&times;</span>
  </div>
<% } %>

 

๐Ÿง views/dashboard.ejs

<h1 class="mt-4">Dashboard</h1>
<p class="lead mb-3">Welcome User! <%= name %></p>
<a href="/users/logout" class="btn btn-secondary">Logout</a>

 

๐Ÿง views/layout.ejs

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
    integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
  <link rel="stylesheet" href="https://bootswatch.com/4/journal/bootstrap.min.css" />
  <title>Node.js ๋กœ ๋กœ๊ทธ์ธ ๋ฐ ํšŒ์›๊ฐ€์ž…</title>
</head>

<body>
  <div class="container">
    <%- body %>
  </div>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
    integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
    crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js"
    integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut"
    crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js"
    integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k"
    crossorigin="anonymous"></script>
</body>

</html>

 

๐Ÿง views/login.ejs

<div class="row mt-5">
  <div class="col-md-6 m-auto">
    <div class="card card-body">
      <h1 class="text-center mb-3"><i class="fas fa-sign-in-alt"></i>Login</h1>
      <%-include('./partials/messages.ejs') %>
      <form action="/users/login" method="POST">
        <div class="form-group">
          <label for="email">Email</label>
          <input
            type="email"
            id="email"
            name="email"
            class="form-control"
            placeholder="Enter Email"
          />
        </div>
        <div class="form-group">
          <label for="password">Password</label>
          <input
            type="password"
            id="password"
            name="password"
            class="form-control"
            placeholder="Enter Password"
          />
        </div>
        <button type="submit" class="btn btn-primary btn-block">Login</button>
      </form>
      <p class="lead mt-4">No Account? <a href="/users/register">Register</a>
      </p>
    </div>
  </div>
</div>

 

๐Ÿง views/register.ejs

<div class="row mt-5">
  <div class="col-md-6 m-auto">
    <div class="card card-body">
      <h1 class="text-center mb-3"><i class="fas fa-user-plus"></i>Register</h1>
        <%-include('./partials/messages.ejs') %>
        <!-- POST๋กœ ๋ฐ›์•„์š” -->
        <form action="/users/register" method="POST">
          <div class="form-group">
            <label for="name">Name</label>
            <input 
              type="name"
              id="name" 
              name="name"
              class="form-control"
              placeholder="Enter Name"
              value="<%= typeof name != 'undefined' ? name : '' %>" />
          </div>
          <div class="form-group">
            <label for="email">Email</label>
            <input
              type="email"
              id="email"
              name="email"
              class="form-control"
              placeholder="Enter Email"
              value="<%= typeof email != 'undefined' ? email : '' %>" />
          </div>
          <div class="form-group">
            <label for="password">Password</label>
            <input
              type="password"
              id="password" 
              name="password" 
              class="form-control" 
              placeholder="Create Password"
              value="<%= typeof password != 'undefined' ? password : '' %>" />
          </div>
          <div class="form-group">
            <label for="password2">Confirm Password</label>
            <input 
              type="password" 
              id="password2"
              name="password2" 
              class="form-control"
              placeholder="Confirm Password"
              value="<%= typeof password2 != 'undefined' ? password2 : '' %>" />
          </div>
          <button type="submit" class="btn btn-primary btn-block">
            Register
          </button>
        </form>
        <p class="lead mt-4">Have An Account? <a href="/users/login">Login</a></p>
    </div>
  </div>
</div>

 

๐Ÿง views/welcome.ejs

<div class="row mt-5">
  <div class="col-md-6 m-auto">
    <div class="card card-body text-center">
      <!-- app.js ์—์„œ express.static ๋ฏธ๋“ค์›จ์–ด ํ•จ์ˆ˜์— ์ „๋‹ฌํ•˜๋ฉด ํŒŒ์ผ์˜ ์ง์ ‘์ ์ธ ์ œ๊ณต์„ ์‹œ์ž‘ -->
      <center><img src="/images/alpaca.jpg" width="100px" height="100px"></center>
      <p style="margin-top: 16px;">๋กœ๊ทธ์ธ & ํšŒ์›๊ฐ€์ž…</p>
      <a href="/users/register" class="btn btn-primary btn-block mb-2">Register</a>
      <a href="/users/login" class="btn btn-secondary btn-block">Login</a>
    </div>
  </div>
</div>

 

๐Ÿง app.js

const express = require("express");
const expressLayouts = require("express-ejs-layouts");
const mongoose = require("mongoose");
const flash = require("connect-flash");
const session = require("express-session");
const passport = require("passport");
const app = express();

require("dotenv").config();
require("./middlewares/passport")(passport);

// DB config
const db = process.env.MONGODB_URI;

// connect to Mongo
mongoose.connect(db, {
  useNewUrlParser: true,    // useNewUrlParser : ์—๋Ÿฌ ๋ฐฉ์ง€
  useUnifiedTopology: true
})
  .then(() => console.log("๐Ÿ’šMongoDB Connected..."))
  .catch(err => console.log(err));

// ejs ๋ฏธ๋“ค์›จ์–ด
app.use(expressLayouts);
// express ์˜ view ์—”์ง„์„ ejs ๋กœ ์„ธํŒ…
app.set("view engine", "ejs");

// Bodyparser
// express์„œ๋ฒ„๋กœ POST์š”์ฒญ์„ ํ•  ๋•Œ inputํƒœ๊ทธ์˜ value๋ฅผ ์ „๋‹ฌ
// URL-encoded ํ˜•์‹์˜ ๋ฌธ์ž์—ด๋กœ ๋„˜์–ด์˜ค๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ์ฒด๋กœ์˜ ๋ณ€ํ™˜ ํ•„์š”
app.use(express.urlencoded({ extended: false }));

// Express session
// Express ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์„ธ์…˜์„ ๊ด€๋ฆฌํ•˜๊ธฐ ์œ„ํ•œ ๋ฏธ๋“ค์›จ์–ด
// Server์—์„œ Client์—๊ฒŒ ์ฟ ํ‚ค๋กœ sessionID๋ฅผ ๋ฐœ๊ธ‰ํ•ด์ฃผ๊ณ ,
// ์ด ์ฟ ํ‚ค๋ฅผ ํ†ตํ•ด Server์— ์ ‘์†ํ•˜๋ฉด sessionID๊ฐ’์„ ํ™œ์šฉํ•ด ์–ด๋–ค Client์ธ์ง€ ์‹๋ณ„ํ•˜๊ณ  ๊ด€๋ จ ์ •๋ณด๋ฅผ ์ œ๊ณต
app.use(session({
  secret: 'secret',
  resave: true,
  saveUninitialized: true,
}));

// passport ์ดˆ๊ธฐํ™” ๋ฐ session ์—ฐ๊ฒฐ ๋ฏธ๋“ค์›จ์–ด
app.use(passport.authenticate('session'));
app.use(passport.initialize());
app.use(passport.session());

// Connect flash
// ํ•œ ๋ฒˆ ์ถœ๋ ฅ๋˜๊ณ  ์‚ฌ๋ผ์ง€๋Š” ๋ฉ”์‹œ์ง€
// session ๋ณด๋‹ค ์•„๋ž˜์ชฝ์—์„œ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์„ค์น˜
// flash ๋ฉ”์‹œ์ง€๋Š” Queue์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ๋ฅผ ๊บผ๋‚ด๋Š” ์ฆ‰์‹œ, session์—์„œ ์‚ญ์ œ
app.use(flash());

// Global Vars
app.use((req, res, next) => {
  // res.locals : ๋ทฐ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š” ๊ธฐ๋ณธ ์ฝ˜ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฐ์ฒด
  res.locals.success_msg = req.flash("success_msg");
  res.locals.error_msg = req.flash("error_msg");
  res.locals.error = req.flash("error");
  next();
});

// uploads ํด๋”์˜ ์‚ฌ์ง„ html์— ๋ณด์ด๊ฒŒ ํ•˜๊ธฐg
app.use(express.static('public'));

// Routes
app.use("/", require("./routes/index"));
app.use("/users", require("./routes/user"));

const PORT = process.env.PORT || 8000;

app.listen(PORT, console.log(`๐Ÿš€Server started on port http://localhost:${PORT}`));

 

๐Ÿง .env

APPLICATION_NAME=gani
PORT=3000
MONGODB_URI=mongodb+srv://~~~
SECRET=jVTKzz8w1@k^Lrvm*52w

 

๐Ÿง package.json

{
  "name": "passportauthentication",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js",
    "dev": "nodemon app.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcryptjs": "^2.4.3",
    "connect-flash": "^0.1.1",
    "dotenv": "^16.0.3",
    "ejs": "^3.1.9",
    "express": "^4.18.2",
    "express-ejs-layouts": "^2.5.1",
    "express-session": "^1.17.3",
    "mongoose": "^7.0.1",
    "passport": "^0.6.0",
    "passport-local": "^1.0.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.21"
  }
}

 

728x90
๋ฐ˜์‘ํ˜•
Comments