<PassportJS> Passport-Login & Logout

김민석·2021년 1월 15일
0

YouTube clone

목록 보기
37/54

저번시간까지 해서 user register(join)단계까지 완료했습니다. 이번 시간에는 등록한 user를 login시키는 방법을 알아봅니다.

Login 구현

project

youtube
 *|passport.js
 *|app.js
  |routers
   *|globalRouter
  |controllers
   *|userController
 *|middlewares.js
 *|.env
  |views
    |partials
     *|header.pug

globalRouter

join이 성공적으로 이루어지면 바로 사용자를 postLogin 시키기위해 아래와 같이 작성합니다.

globalRouter.post(routes.join, postJoin, postLogin);

userController

  • postJoin
    함수를 middleware로 만들기 위해 next 추가
  • passport.authenticate('stratey명', {})
    strategy를 전달하면 인증 과정이 이루어집니다. 로그인 성공시와 실패시에 따른 redirect 주소를 option 객체로 전달할 수 있습니다. 참고로 LocalStrategy의 strategy명은 'local'입니다.
import passport from "passsport";

export const postJoin = async (req, res, next) => {
  const {body:{name, email, password, password2}} = req;
  
  if(password !== password2){
    res.status(400);
    res.render("join", {pageTitle: "Join"});
  }else{
     try{
       const user = await User({name, email});
       await User.register(user, password);
       next();
     }catch(e){
       console.log(e);
       res.render("join", {pageTitle: "Join"});
     }
  }
};

//body 정보를 postJoin이 넘긴다 생각하고 postLogin은 username과 password를 알아서 찾기 때문에 따로 전달해주지 않아도 되나??
export const postLogin = passport.authenticate('local', {
  failureRedirect: routes.login,
  successRedirect: routes.home
});

passport.js

serialize

serialize란 어떤 정보를 쿠키에게 포함시킬지를 의미합니다.

위와 같은 형태가 쿠키이구요. 이름이 있고 값이 있죠. 쿠키는 매우 작아야하고 개인정보같은 민감한 정보를 담아선 안됩니다.

deserialize

deserialize는 쿠키의 정보를 어떻게 실제로 사용할 정보로 전환하는지를 의미합니다. 쿠키로부터 user의 id를 받았다면 id를 통해 실제 user 정보를 가져오는 과정이라고 생각하면 됩니다. deserialize된 유저 정보는 req.user에 담기게 됩니다.

구현

serialize와 deserialize모두 passport-local-mongoose를 plugging하면 추가되는 method로 구현할 수 있습니다.

  • passport.serializeUser()
    로그인을 하면 호출되는 함수로 passport가 serialize 과정을 통해 session으로 보낼 식별자를 설정합니다.(식별자로 id, 이메일 등등 많은 것이 될 수 있지만 쿠키의 크기가 작을수록 이상적이며 민감한 정보를 담아선 안됩니다)
    아래 이미지는 식별자로 email을 선택한 경우 입니다.
  • passport.deserializeUser()
    passport가 deserialize 과정을 통해 session에 있는 정보를 바탕으로 user의 정보를 불러옵니다.(User.findOnd({id})와 같은 방법으로)
    deserializeUser는 모든 페이지에 대해 방문할 때마다 실행되어 로그인한 user의 정보를 req.user에 담아둡니다.
  • User.serializeUser()
    passport-local-mongoose를 plugging하여 존재하는 mehtod로 Passport가 serialize 과정을 통해 session으로 id를 보내는데 사용됩니다.
  • User.deserializeUser()
    passport-local-mongoose를 plugging하여 존재하는 mehtod로
    Passport가 session의 id를 통해 사용자의 정보를 deserialize하는데 사용됨

대부분의 사람들이 위의 과정처럼 쿠키에 id를 보내고 id로 사용자를 식별합니다.

import passport from 'passport';
import User from './models/User';

//passport-local-mongoose가 제공하는 전략(shortcut)
passport.use(User.createStrategy());
//쿠키에 담을 정보에 대한 설정을 passport-local-mongoose가 대신 해줍니다.
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

app.js

  • express-session
    session을 관리하기 위해 필요합니다.
  • passport.initialize()
    express에서 passport를 사용하겠다는 의미입니다.
  • passport.session()
    passport가 session위에서 사용되도록 하겠다는 middleware입니다.
    session을 사용하게 되면 serialize과정과 deserialize과정이 일어나게 되고 해당 과정에 대한 코드는 passport.js에 담겨있으므로 passport.js를 import해주어야 합니다.

deserializeuSer(passport.js에서 config 해준)를 통해 user에 대한 정보를 얻어 req.user에 담아주게 됩니다.

import session from 'express-session';
import passport from 'passport';
import './passport';//passport 설정 파일

app.use(session({
  secret: process.env.SECRET,
  resave: true,
  saveUninitialize: false
}));
/*app.use(cookieParser());다음에 적어주어야합니다*/
app.use(passport.initialize());
app.use(passport.session());

주의사항

이 단계까지는 서버를 재시작 한다음에 로그인이나 회원가입을 테스트 해보려면 꼭 쿠키를 삭제하셔야됩니다. 아니면 오류가 생겨서 정상적인 테스트가 어려울 수 있습니다.

middlewares.js

탬플릿에 user에 대한 정보를 전달하기 위해 res.locals.user = req.user || null로 수정해줍니다.

export const localsMiddleware = (req, res, next) =>{
  res.locals.siteName = "Wetube";
  res.locals.routes =  routes;
  //req.user가 없다면 null을 넣는다는 뜻
  res.locals.user = req.user || null;
  next();
}

header.pug

user.isAuthenticated를 없앴으므로 코드를 변경해줍니다.

if !user
  //login join template
else
  //logout upload template

.env

SECRET에 랜덤 문자열 추가해줍니다.(session 옵션으로 사용)

SECRET = "qwfsdgjasdlkfjalsd"

로그인한 유저와 비로그인 유저의 접근 가능한 페이지 구분 구현

project

youtube
 *|middlewares.js
  |routers
   *|globalRouter
   *|userRouter
   *|videoRouter

middlewares.js

middlewares에 새로운 middleware onlyPublic을 추가합니다.
로그인한 유저만 접근가능하게 하는 onlyPrivate, 로그인되지 않은 유저만 접근 가능하게 하는 onlyPublic 총 2개의 middleware를 만듭니다.

export const onlyPublic = (req, res, next) => {
  //로그인 되어있을 경우
  if(req.user){
    //home으로 redirect
    res.redirect(routes.home);
  }else{
    //그렇지 않다면 원래 요청에 대한 응답해주기 위해 next()
    next();
  }
}

exprot const onlyPrivate = (req, res, next) => {
  if(!req.user){
    res.redirect(routes.home);
  }else{
    next();
  }
}

globalRouter.js

import {onlyPublic} from '../middlewares';

globalRouter.get(routes.join, onlyPublic, getJoin);
globalRouter.post(routes.join, onlyPublic, postJoin);

globalRouter.get(routes.login, onlyPublic, getLogin);
globalRouter.post(routes.login, onlyPublic, postLogin);

userRouter.js

import {onlyPrivate} from '../middlewares';

userRouter.get(routes.editProfile, onlyPrivate, editProfile);
userRouter.get(routes.changePassword, onlyPrivate, changePassword);
userRouter.get(routes.userDetail(), onlyPrivate, userDetail);

videoRouter.js

videoRouter에서도 editVideo, uploadVideo, deleteVideo에 모두 onlyPrivate middleware를 넣어줍니다.

Logout

userController.js

passport를 사용하면 로그아웃 기능을 손쉽게 구현할 수 있습니다. logout URL에 접근할 때 req.logout()을 실행해주면 로그아웃이 완료됩니다. 세션 관련 작업도 자동으로 처리해줍니다.

export const logout = (req, res) => {
	req.logout();
  	res.redirect(routes.home);
}
profile
누구나 실수 할 수 있다고 생각합니다. 다만 저는 같은 실수를 반복하는 사람이 되고 싶지 않습니다. 같은 실수를 반복하지 않기 위해 기록하여 기억합니다.🙃

0개의 댓글