Node.js

패스포트 사용하기 (로그인 과정 helper)

쿠와와 2021. 1. 5. 15:51

로그인 과정을 쉽게 처리할 수 있게 도와주는 Passport 설치하기

  • 비밀번호 암호화를 위한 bcrypt도 같이 설치
  • 설치 후 app.js와도 연결
  • passport.initialize(): 요청 객체에 passport 설정을 심음
  • passport.session(): req.session 객체에 passport 정보를 저장
  • express-session 미들웨어에 의존하므로 이보다 더 뒤에 위치해야 함

 

passport/index.js 작성

  • passport.serializeUser: req.session 객체에 어떤 데이터를 저장할 지 선택, 사용자 정보를 다 들고 있으면 메모리를 많이 차지하기 때문에 사용자의 아이디만 저장
  • passport.deserializeUser: req.session에 저장된 사용자 아이디를 바탕으로 DB 조회로 사용자 정보를 얻어낸 후 req.user에 저장

 

패스포트 처리 과정

 

로그인 과정

1. 로그인 요청이 들어옴

2. passport.authenticate 메서드 호출

3. 로그인 전략 수행(전략은 뒤에 알아봄)

4. 로그인 성공 시 사용자 정보 객체와 함께 req.login 호출

5. req.login 메서드가 passport.serializeUser 호출

6. req.session에 사용자 아이디만 저장

7. 로그인 완료

 

로그인 이후 과정

1. 모든 요청에 passport.session() 미들웨어가 passport.deserializeUser 메서드 호출

2. req.session에 저장된 아이디로 데이터베이스에서 사용자 조회

3. 조회된 사용자 정보를 req.user에 저장

4. 라우터에서 req.user 객체 사용 가능

 

 

로컬 로그인 구현하기

passport-local 패키지 필요

  • 로컬 로그인 전략 수립
  • 로그인에만 해당하는 전략이므로 회원가입은 따로 만들어야 함
  • 사용자가 로그인했는지, 하지 않았는지 여부를 체크하는 미들웨어도 만듦

 

회원가입 라우터

routes/auth.js 작성

  • bcrypt.hash로 비밀번호 암호화
  • hash의 두 번째 인수는 암호화 라운드
  • 라운드가 높을수록 안전하지만 오래 걸림
  • 적당한 라운드를 찾는 게 좋음
  • ?error 쿼리스트링으로 1회성 메시지

passport/localStrategy.js 작성

  • usernameFieldpasswordFieldinput 태그의 name(body-parserreq.body)
  • 사용자가 DB저장되어있는지 확인한 후 있다면 비밀번호 비교(bcrypt.compare)
  • 비밀번호까지 일치한다면 로그인

 

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) => {    // session에 유저 id만 저장 
    done(null, user.id);    // 왜? 메모리가 한정되어 있기 때문에 회원정보가 넘 많으니 id 만
  });

  passport.deserializeUser((id, done) => {
    User.findOne({ where: { id } })
      .then(user => done(null, user))
      .catch(err => done(err));
  });

  local();    // passport/localStrategy.js 로 감
  kakao();
};

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',   // req.body.email
    passwordField: 'password',    // req/body.passwoed
  }, 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);
    }
  }));
};

 

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); // 해쉬화 12개의 숫자로 
    await User.create({
      email,
      nick,
      password: hash,
    });
    return res.redirect('/');   // 메인 페이지로 돌아감 
  } catch (error) {
    console.error(error);
    return next(error);
  }
});

router.post('/login', isNotLoggedIn, (req, res, next) => {
  // passport 가 loacl을 찾음 // passport 의 index에 있는 local ()
  passport.authenticate('local', (authError, user, info) => {  // done() 함수가 호출 되야 일로옴 
    if (authError) {
      console.error(authError);
      return next(authError);
    }
    if (!user) {
      return res.redirect(`/?loginError=${info.message}`);
    }
    return req.login(user, (loginError) => {    // 사용자 정보를 넣어줌 passport 의 index
      if (loginError) {
        console.error(loginError);
        return next(loginError);
      }
      return res.redirect('/');
    });
  })(req, res, next); // 미들웨어 내의 미들웨어에는 (req, res, next)를 붙임
});

router.get('/logout', isLoggedIn, (req, res) => {
  req.logout();
  req.session.destroy();
  res.redirect('/');
});

router.get('/kakao', passport.authenticate('kakao'));

router.get('/kakao/callback', passport.authenticate('kakao', {
  failureRedirect: '/',
}), (req, res) => {
  res.redirect('/');
});

module.exports = router;

routes/middlewares.js

// 로그인을 했는지 안해줬는지 판별해주는 middleware

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}`);
  }
};

'Node.js' 카테고리의 다른 글

multer 사용해서 이미지 업로드 구현  (0) 2021.01.06
카카오 로그인 작업  (0) 2021.01.06
Nodejs 데이터 베이스 셋팅하기  (0) 2021.01.05
프로젝트 구조 갖추기  (0) 2021.01.03
시퀄라이즈 쿼리  (0) 2020.12.30