✅ passport 모듈
회원가입과 로그인을 구현할 때
세션과 쿠키 처리등의 복잡한 작업을 쉽게 해주는 모듈
검증되어 있어 안전하게 사용할 수 있다
passport 모듈로 카카오, 구글, 페이스북 로그인을 위한 OAuth 도 사용 가능하다
✅ 의존성 추가 및 설치
npm i passport passport-local passport-kakao bcrypt
🔨 app.js
const passport = require('passport');
...
const passportConfig = require('./passport');
...
passportConfig();
app.use(passport.initialize());
app.use(passport.session());
passport.initialize 미들웨어는 req 객체에 passport 설정을 심어준다
passport.session 미들웨어는 req.session 객체에 passport 정보를 저장한다
이 때, req.session 객체는 express-session 미들웨어에서 생성하기 때문에
express-session 뒤에 passport 미들웨어를 연결해야 한다.
✅ passport 적용
🔨 ./passport/index.js
const passport = require('passport');
const local = require('./localStrategy');
const kakao = require('./KakaoStrategy');
const User = require('../models/user');
module.exports = () => {
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done)=>{
User.findOne({where: { id })
.then(user => done(null, user))
.catch(err => done(err));
});
local();
kakao();
};
serializeUser는 req.session 객체에 어떤 데이터를 저장할지 정하는 메소드다
로그인 시에만 실행된다
done 함수의 첫 번째 인수는 에러 발생 시,
두 번째 인수는 세션에 저장하고 싶은 데이터를 위해 사용한다
deserializeUser는 매 요청마다 실행된다
serializeUser 의 done 에서 사용한 두 번째 인수가 매개변수로 온다
이를 req.user 에 저장하여 이제부터 req.user로 사용자 정보를 받아올 수 있다
✅ 로그인 여부 확인용 미들웨어 추가
🔨 ./routes/middlewares.js
exports.isLoggedIn = (req, res, next)=>{
if (req.isAuthenticated()){
next();
} else {
res.status(403).send('로그인이 필요함');
}
}
exports.isNotLoggedIn = (req, res, next)=>{
if (!req.isAuthenticated()){
next();
} else {
const message = encodeURIComponent('로그인한 상태');
res.redirect(`/?error=${message}`);
}
}
로그인한 사용자는 회원가입과 로그인 라우터에 접근하면 안된다
로그인하지 않은 사용자는 로그아웃 라우터에 접근하면 안된다
이를 제어하기 위한 미들웨어 두 가지를 추가한다
req.isAuthenticated 메서드는 passport 모듈이 추가해준다
이 메서드로 로그인 여부를 파악할 수 있다
🔨 ./routes/pages.js
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
...
router.get('/profile', isLoggedIn, (req, res) => {
res.render('profile', {title: '내 정보 - nodebird'});
});
router.get('/join', isNotLoggedIn, (req, res) => {
res.render('join', {title: '회원가입 - nodebird'});
});
라우터에 관련 미들웨어를 추가해준다
중간에 미들웨어를 추가해줘서 값이 true 일때만 next를 호출하게 된다
✅ auth 관련 라우팅
🔨 ./routes/auth.js
const express = require('express');
const passport = require('passport');
const bcrypt = require('bcrypt');
const { isLoggedIn, isNotLoggedIn } = require('./middlewares');
const User = require('../models/user');
const router = express.Router();
모듈 추가
router.post('/join', isNotLoggedIn, async (req, res, next) =>{
const { email, nick, password } = req.body;
try{
const exUser = await User.findOne({ where: {email}});
if (exUser){
return res.redirect('/join?error=exist');
}
const hash = await bcrypt.hash(password, 12);
await User.create({
email,
nick,
password: hash,
});
return res.redirect('/');
} catch (error){
console.error(error);
return next(error);
}
});
로그인 되어 있지 않은 상태에서만 회원가입 창에서 post 요청을 받는다
이미 db에 이메일이 있는 유저라면 error와 함께 리다이렉션 시킨다
비밀번호는 bcrypt 로 암호화 하여 db에 저장시킨다
router.post('/login', isNotLoggedIn, (req, res, next)=>{
passport.authenticate('local', (authError, user, info)=>{
if (authError){
console.error(authError);
return next(authError);
}
if (!user){
return res.redirect(`/?loginError${info.message}`);
}
return req.login(user, (loginError)=>{
if (loginError){
console.error(loginError);
return next(loginError);
}
return res.redirect('/');
});
})(req, res, next);
});
로그인은 passport.authenticate('local') 미들웨어로 로컬 로그인 전략을 수행한다
미들웨어가 중첩되어 있는 상태에서는 마지막에 인수로 (req, res, next)를 제공해야 한다
authError 값이 있다면 호출이 실패한 것이다
없다면 req.login 메서드를 호출한다
이는 passport 모듈에서 req 객체에 login과 logout 메서드를 추가시켰기 때문에 가능하다
req.login 은 passport.serializeUser 를 호출한다
이 때 req.login 에 전달한 user 객체가 serializeUser 의 인수로 넘어간다
router.get('/logout', isLoggedIn, (req, res) =>{
req.logout();
req.session.destroy();
res.redirect('/');
});
로그아웃 라우터는 req.logout 메서드를 호출하여 req.user 객체를 제거한다
req.session.destory로 session 객체의 내용을 제거한다
이 후 메인 페이지로 리다이렉션 시킨다
✅ local 로그인 전략 구현
🔨 ./passport/localStrategy.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt');
const User = require('../models/user');
module.exports = ()=>{
passport.use(new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
}, async(email, password, done) =>{
try{
const exUser = await User.findOne({where: {email}});
if(exUser){
const result = await bcrypt.compare(password, exUser.password);
if (result){
done(null, exUser);
} else{
done(null, false, {message: '비밀번호가 일치하지 않음'});
}
} else{
done(null, false, {message: '가입되지 않은 회원'});
}
} catch (error){
console.error(error);
done(error);
}
}));
};
LocalStrategy 생성자에 인수를 두 개 준다
첫 번째 인수는 전략에 관한 설정을 하는 곳이다
usernameField와 passwordField 에 일치하는 로그인 라우터의
req.body 의 속성명을 적어준다
두 번째 인수는 실제 전략을 수행하는 함수다
첫 번째 인수에서 넣어준 속성이 두 번째 인수의 매개변수가 된다
세 번째 done 함수는 passport.authenticate 의 콜백 함수다
✅ 라우터 연결
const authRouter = require('./routes/auth');
...
app.use('/auth', authRouter);
연결이 완료되었다
'📡 백엔드 > ⭐ Node.js' 카테고리의 다른 글
[Nodejs] Sequelize 로 SQL 쿼리 사용하기 (0) | 2022.05.21 |
---|---|
[Nodejs] Sequelize 로 SQL 데이터베이스 연동하기 (0) | 2022.05.21 |
[Nodejs] Router 객체로 라우팅 파일 분리 (0) | 2022.05.17 |
[Nodejs] 미들웨어의 사용과 자주 쓰이는 미들웨어 (0) | 2022.05.16 |
[Nodejs] express 서버 열기 (0) | 2022.05.15 |