(MERN 스택) 회원가입, 로그인, 로그아웃 기능 구현하기 - passport / 쿠키, 세션

채희태·2022년 9월 27일
0

회원가입 시 bcrypt로 암호화 하기

db에 저장 시 비밀번호를 암호화해 보안해야 한다.
이 때, bcrypt 라이브러리를 사용해 간단하게 암호화 할 수 있다.
npm i bcrypt

import express from "express";
import bcrypt from "bcrypt";

const router = express.Router();

router.post("/", (req, res) => {
  const hashedPassword = bcrypt.hash(req.body.password, 10);
});

숫자가 높을 수록 보안성이 좋아진다. 하지만 너무 높으면 성능이 낮아지므로 10~12를 사용한다.

passport 설정 적용

passport를 사용하기 위해 app에 passport설정을 적용한다.

server/app.js

import passportConfig from './passport/index'
//passport 연결
passportConfig()

passport로 로그인

패스포트는 다양한 로그인 전략을 한번에 관리해준다.
설치하기
npm i passport passport-local
폴더 및 파일 생성
server/passport 폴더 생성
passport폴더에 index.js, local.js 파일 생성

server/passport/local.js

import passport from 'passport'
import { Strategy : LocalStrategy } from 'passport-local'
import { User } from '../models'
import bcrypt from 'bcrypt'

module.exports = () => {
  //로그인 리퀘스트 정보
  passport.use(new LocalStrategy({
    //로그인 리퀘스트시 action 데이터로 email, password를 보냄. 
    //ㄴ> req.body.email, req.body.password
    usernameField: 'email',
    passwordField: 'password',
  }, async (email, password, done) => {
    //로그인 전략 짜기
    try {
      //User모델에서 해당 email을 찾음.
      const user = await User.findOne({
        where: { email : email }
      });
	  //User모델에 해당 email이 없다면
      if(!user){
        //우선 done에 관련 정보를 넣어준다.
        //순서대로 서버 에러, 성공여부, 클라이언트 에러
        return done(null, false, { reason : '존재하지 않는 사용자입니다!'}) 
      }
      //해당 email이 있다면, 비밀번호 비교.
      //리퀘스트된 password와 db에 저장된 user의 password를 비교.
      //비교 했는데 일치하면 true, 일치하지 않으면 false를 리턴함.
      const result = await bcrypt.compare(password, user.password);
      //result가 true면,
      if(result){
        // 로그인 성공
        //순서대로 서버 에러, 성공여부
        //성공 여부에 사용자 정보를 넣어줌.
        return done(null, user);
      }
      //비밀번호가 일치하지 않을 때,
      //클라이언트 에러에 에러 정보를 넣어줌.
      return done(null, false, { reason : '비밀번호가 틀렸습니다.' });   
   //서버에러시
   } catch(err) {
     //서버에러 여부에 error를 넣어줌.
     console.error(err);
     return done(err);
   }
    }));
};

로그인 전략은 서버 에러 여부를 try, catch로 나누고,
서버 에러가 없을 경우, 이메일 있는지 검사 -> 비밀번호 일치하는지 검사의 flow로 로그인 해준다.

passport 실행

만들어 놓은 passport를 사용해 api서버에서 로그인 검증을 할 수 있다.

server/routes/login.js

import passport from 'passport'

//done(서버에러, 성공여부, 클라이언트에러)를 받아옴.
router.post('/login', passport.authenticate('local', (ctErr, userInfo, svrErr) => {
  //서버에러 처리
  if(err){
   console.error(err);
  }
});

만들어 놓은 로그인 전략이 실행됨.

미들웨어 확장

미들웨어 확장은 express의 기법이다.
passport.authenticate를 사용하면 express의 req,res,next기능을 사용하지 못하는데 이를 덮어씌움으로써 req,res,next를 사용할 수 있게 해준다.

router.post('/login', (req,res, next)=> {
  passport.authenticate('local', (ctErr, userInfo, svrErr) => {
  //서버 에러시
  if(svrErr){
    console.error(err);
    return next(err);
  }
  //클라이언트 에러시 (이메일 또는 비밀번호 틀렸을 때)
  if(ctErr){
    //401은 허가되지 않음을 의미. (비인증)
    return res.status(401).send(ctErr.reason);
  }   
  //로그인 성공시 user에 사용자 정보가 들어있음.
  //req.login으로 passport를 통한 로그인을 할 수 있음. (passport기능)
  return req.login(userInfo, async(passPortErr) => {
    //passport 로그인 시 생기는 에러 핸들링.
    if(passPortErr){
      console.error(passPortErr);
      return next(passPortErr);
    }
    //사용자 정보 프론트로 넘기기.
      return res.status(200).json(userInfo);
    })
  })(req, res, next);
});

세션/쿠키로 로그인 정보 저장

로그인 시 브라우저/서버에서 같은 유저 정보를 갖고 있어야 한다.
서버에서 로그인된 유저의 정보를 브라우저로 보내주는데, 이 때 암호화된 문자열을 쿠키에 담아 보내준다.
그리고 이 쿠키는 브러우저에서 서버로 api를 요청할 때 서버에서 로그인된 유저가 누군지 판별하기 위해 사용된다.
설치하기
npm i express-session
npm i cookie-parser

세션/쿠키 설정하기

세션/쿠키를 설정할 때, secret문자열의 경우 중요한 보안 사항이므로
dotenv를 사용해 준다.

.env

COOKIE_SECRET = 'secretKey'

server/app

//dotenv 세팅
const dotenv = require('dotenv');

dotenv.config();

app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
    saveUninitialized: false,
    resave: false,
    secret: process.env.COOKIE_SECRET,
});
app.use(passport.initialize());
app.use(passport.session());

쿠키/세션 serialize 해주기.

서버에서는 로그인된 유저의 모든 정보 데이터를 갖고 있으면 메모리가 너무 많이 소모된다.
이를 해결하기 위해 serialize를 사용한다.
세션에서 유저 정보의 id만 갖고 있음. => serializeUser
user의 id를 원래대로 복원함. => deserializeUser

server/passport/index.js

const passport = require('passport');
const local = require('./local');
const { User } = require('../models');

module.exports = () => {
  //서버 세션에 유저의 id만 저장함.
  passport.serializeUser((user, done) => {
    done(null, user._id); 
  });
  //id로 사용자 정보 복원하기.
  passport.deserializeUser(async (id, done) => {
    try {
      const user = await User.findOne({ where : { id }});
      //user는 req.user로 들어감.
      //이 req.user는 유저 정보가 필요할 때 api에서 사용됨.
      done(null, user);
    } catch(error) {
      console.error(error);
      done(error);
    }
  });
  local();
};
profile
기록, 공부, 활용

0개의 댓글