passport 라이브러리 설치
passport는 node 앱에 인증을 추가해주는 유명한 도구이다. 구글이나 트위터, 페이스북 같은 계정으로도 로그인 할 수 있게 해준다. 이 프로젝트에서는 로컬 계정으로만 로그인 할 수 있게 구현한다.
npm i passport passport-local passport-local-mongoose
사용자 계정 모델
User 스키마를 정의할 때 사용자 이름과 비밀번호는 지정하지 않는다. plugin이 스키마에 사용자 이름과 비밀번호를 추가해주기 때문이다.
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const Schema = mongoose.Schema;
const UserSchema = new Schema({
email: {
type: String,
required: true,
unique: true
}
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', UserSchema);
Passport 설정
passport.session()은 session()을 사용하기 때문에 app.use(session()) 이후에 존재해야 한다.
new LocalStrategy(User.authenticate()) 부분에서는 LocalStrategy를 요청하며 사용자 모델에 대한 인증을 추가한다.
passport.serializeUser(User.serializeUser()) 는 passport에게 사용자를 어떻게 직렬화하는지 알려주고, 직렬화는 어떻게 데이터를 얻고 세션에서 사용자를 저장하는지 참조한다.
const passport = require('passport');
const LocalStrategy = require('passport-local');
const User = require('./models/user');
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
등록 경로
새로운 라우터 user.js를 생성하고 app.js에 필요한 코드 적당히 잘 끼워넣는다.
User.register에서 사용자 이름과 비밀번호 암호화, save 까지 수행해준다. 성공적으로 계정이 생성되었으면 성공했다는 flash 메시지를 띄워주고 사용자 이름이나 이메일이 중복되어서 계정 생성에 실패하면 에러 메시지를 띄운다. try catch 구문을 안쓰면 못생긴 에러가 cmd에 와장창 뜬다.
app.js
const User = require('./models/user');
registerRoutes = require('./routes/users');
app.use('/', registerRoutes);
user.js
const express = require('express');
const router = express.Router();
const catchAsync = require('../utils/catchAsync');
const ExpressError = require('../utils/ExpressError');
const User = require('../models/user');
router.get('/register', (req, res) => {
res.render('users/register');
})
router.post('/register', async (req, res) => {
try {
const { email, username, password } = req.body;
const user = new User({ email: email, username: username });
const registeredUser = await User.register(user, password);
req.flash('success', 'Welcome to Yelp Camp!');
res.redirect('/campgrounds')
} catch (e) {
req.flash('error', e.message);
return res.redirect('/register');
}
})
module.exports = router;
로그인 경로
등록 경로 만들면서 생성한 user.js에 로그인 경로도 추가한다.
authenticate로 전달받은 정보를 인증한다. local은 strategy를 의미하는데, 'google' 이나 'facebook' 같은 것을 쓸 수 있다. 여기서는 로컬 계정만 쓰기로 했으므로 local을 쓴다. failureFlash를 true로 설정하여 로그인에 실패할 경우 flash 메시지를 자동으로 띄워준다. failureRedirect는 로그인에 실패했을시 redirect할 경로를 의미한다.
router.get('/login', (req, res) => {
res.render('users/login');
})
router.post('/login', passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), async (req, res) => {
req.flash('success', 'Welcome Back!');
res.redirect('/campgrounds');
})
라우트 보호
로그인을 하지 않으면 새로운 캠핑장을 만들 수 없게 해보자.
passport의 isAuthenticated 메서드를 이용하면 간단하게 할 수 있다.
campground.js
router.get('/new', (req, res) => {
if (!req.isAuthenticated()) {
req.flash('error', 'You must be signed in');
return res.redirect('/login');
}
res.render('new');
})
로그인 상태 미들웨어
로그인 상태를 확인하는 미들웨어를 만들어보자. 캠핑장을 생성하거나 수정, 삭제하고 리뷰를 쓰거나 삭제할 때 로그인이 되어있어야만 가능하도록 한다. campground.js에 아래 코드를 추가하고 new, edit, delete에 isLoggedIn를 미들웨어로 추가한다.
middleware.js
module.exports.isLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
req.flash('error', '해당 기능을 사용하려면 로그인 해야 합니다.');
return res.redirect('/login');
}
next();
}
campground.js
const { isLoggedIn } = require('../middleware');
로그아웃
로그아웃 과정에서 에러가 발생하면 에러 메시지를 띄울 수 있도록 next(err)해주고, 성공적으로 로그아웃 되었다면 flash 메시지를 띄워준다.
users.js
router.get('/logout', (req, res) => {
req.logout(function (err) {
if (err) { return next(err); }
req.flash('success', 'Goodbye!');
res.redirect('/campgrounds');
});
})
로그인 상태에 따라 로그인/로그아웃 표시하기
flash 미들웨어를 정의하는 부분에 res.locals.currentUser = req.user를 추가하고 navbar.ejs에 아래 코드를 추가한다.
app.js
app.use(flash());
app.use((req, res, next) => {
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
})
navbar.ejs
<div class="navbar-nav ms-auto">
<% if (!currentUser) { %>
<a class="nav-link" href="/register">Register</a>
<a class="nav-link" href="/login">Login</a>
<% } else { %>
<a class="nav-link" href="/logout">logout</a>
<% } %>
</div>
작업 페이지로 돌아오기
로그인되지 않았을 때 req.session.returnTo = req.originalUrl로 현재 페이지를 저장하고 로그인 페이지로 redirect 한다. 그리고 로그인 되었을 때 로그인 하기 전 머물렀던 페이지로 redirect 한다.
middleware.js
module.exports.isLoggedIn = (req, res, next) => {
if (!req.isAuthenticated()) {
req.session.returnTo = req.originalUrl;
req.flash('error', '해당 기능을 사용하려면 로그인 해야 합니다.');
return res.redirect('/login');
}
next();
}
module.exports.storeReturnTo = (req, res, next) => {
if (req.session.returnTo) {
res.locals.returnTo = req.session.returnTo;
}
next();
}
users.js
router.post('/login', storeReturnTo, passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), (req, res) => {
req.flash('success', 'Welcome Back!');
delete req.session.returnTo;
res.redirect(res.locals.storeReturnTo || '/campgrounds');
})
'웹 프로그래밍' 카테고리의 다른 글
[Yelpcamp 프로젝트] 이미지 편집 (0) | 2023.12.07 |
---|---|
[Yelpcamp 프로젝트] 이미지 파일 (1) | 2023.12.07 |
[JS] 로그인 상태 유지 (2) | 2023.12.01 |
[Yelpcamp 프로젝트] flash (0) | 2023.11.29 |
[Express] 세션 (0) | 2023.11.17 |