๐ ๊ณต๋ถํ๋ ์ง์ง์ํ์นด๋ ์ฒ์์ด์ง?
[Login & Register authentication with Node js] (9) ์ต์ข ์ฝ๋์ ๋ณธ๋ฌธ
๐ฉ๐ป ๋ฐฑ์๋(Back-End)/Node js
[Login & Register authentication with Node js] (9) ์ต์ข ์ฝ๋์
์ง์ง์ํ์นด 2023. 3. 20. 00:58728x90
๋ฐ์ํ
<๋ณธ ๋ธ๋ก๊ทธ๋ 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">×</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">×</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">×</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">×</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
๋ฐ์ํ
'๐ฉโ๐ป ๋ฐฑ์๋(Back-End) > Node js' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Comments