Elice SW 2/TIL

DAY 34 - TIL(1)

자이구 2022. 5. 21. 23:06

회원가입 구현하기

  • 관리자가 모든 회원의 비밀번호를 알 수 있음 -> 보안 취약점 발생
  • Hash는 문자열을 되돌릴 수 없는 방식으로 암호화하는 방법
  • Hash출력값을 이용해 사용자의 비밀번호를 알아낼 수 없음
  • 비밀번호의 Hash값을 DB에 저장
  • 로그인 시 전달된 비밀번호를 Hash하여 저장된 값과 비교해 로그인 처리

 

SHA1 - 사용법

const crypto = require('crypto');

module.exports = (password) => {
	const hash = crypto.createHash('sha1');
	hash.update(password);
	return hash.digest("hex");
}
  • hash.update() : 주어진 데이터로 해시를 업데이트하는데 사용
  • hasj.digest() : 해시된 데이터를 인코딩 하는데 사용
  • Node.js의 기본제공 모듈인 crypto 모듈을 사용하여 hash값을 얻을 수 있음

 

Passport.js

  • Express.js 어플리케이션에 간단하게 사용자 인증 기능을 구현하게 도와주는 패키지
  • 유저 세션 관리 및 다양한 로그인 방식 추가 가능 

 

passport-local

  • 다양한 로그인 방식을 구현하기 위해 strategy라는 인터페이스 제공
  • strategy 인터페이스에 맞게 설계된 다양한 구현체들이 있음(facebook.,google)
  • passport-local은 username, passport를 사용하는 로그인의 구현체

 

로그인 기능 구현하기

  1. 로그인 화면 구성
  2. passport - local strategy로 로그인 구현
  3. passport.js 설정
  4. passport로 요청 처리

 

passport - local strategy

./passport/strategies/local.js

const LocalStrategy = require('passport-local').Strategy;
const { User } = require('../../models');
const hashPassword = require('../../utils/hash-password');

const config = {
  usernameField: 'email', // 'email' 필드 사용하도록 설정
  passwordField: 'password' // 'password' 필드 사용하도록 설정
};

const local = new LocalStrategy(config, async (email, password, done) => {
  try {
    const user = await User.findOne({ email });
    if (!user) {
      throw new Error('회원을 찾을 수 없습니다.');
    }
    // 검색 한 유저의 비밀번호와 요청된 비밀번호의 해쉬값이 일치하는지 확인
    if (user.password !== hashPassword(password)) {
      throw new Error('비밀번호가 일치하지 않습니다.');
    }

    done (null, {
      shortId: user.shortId,
      email: user.email,
      name: user.name,
    });
  } catch (err) {
    done(err, null);
  }
});

module.exports = local;

 

passport.js 설정

./passport/index.js

const passport = require('passport');
const local = require('./strategies/local')

module.exports = () => {
  passport.use(local);
  // local strategy 사용
};
  • 작성한 strategy를 passport.use를 이용해 사용하도록 선언
  • passport.use를 이용해 strategy를 사용하도록 선언한 후 
  • passport.authenticate를 사용해 해당 strategy를 이용해 요청을 처리할 수 있음 

 

passport.js로 post요청하기

./routes/auth.js

const { Router } = require('express');
const passport = require('passport');

const router = Router();

// passport local 로 authenticate 하기
router.post('/',passport.authenticate('local') ,(req, res, next) => {
  res.redirect('/');
});

module.exports = router;
  • passport.authenticate 함수를 http 라우팅에 연결하면 passport가 자동으로 해당하는 strategy를 사용하는 request handler를 자동 생성 (로그인 처리 - > 세션에 user 저장)

app.js

const session = require('express-session');
const passport = require('passport'); 

app.use(session({ 
  secret: 'elice', 
  resave: false, 
  saveUninitialized: true 
}));

// passport initialize
app.use(passport.initialize());
// passport session 
app.use(passport.session());

app.use('/auth', authRouter);
  • exspress-session과 passport.session()을 사용하면 passport가 로그인 시 유저 정보를 세션에 저장하고 가져오는 동작을 자동으로 수행

 

session 유저 활용

./passport/index.js

const passport = require('passport');
const local = require('./strategies/local')

module.exports = () => {
  passport.use(local);
  // local strategy 사용

  passport.serializeUser((user, callback) => {
    callback(null, user);
  });

  passport.deserializeUser((obj, callback) => {
    callback(null, obj);
  });
};
  • session을 이용해 user를 사용할 때에는 serializeUser 와 deserializeUser를 설정
  • 세션에 user정보를 변환하여 저장하고 가져오는 기능을 제공
  • 세션 사용 시 위 두 함수를 작성하지 않을 시 passport 로그인이 동작하지 않음
  • serializeUser : 사용자 정보 객체를 세션에 아이디로 저장
  • deserializeUser : 세션에 저장한 아이디를 통해 사용자 정보 객체를 불러옴
  • id만 저장하면 세션 용량이 커지는 것을 막음

 

session 유저 활용

./routes/index.js

router.get('/logout', (req, res, next) => {
  req.logout();
  res.redirect('/');
});
  • passport는 req.logout() 함수를 통해 세션의 로그인 정보를 삭제하여, 로그아웃 기능을 제공

 

로그인 확인 미들웨어

./middlewares/login-required.js

module.exports = (req, res, next) => {
if(!req.user){
    res.redirect('/');
    return;
}
next();
  // 로그인이 안되어있다면 메인화면으로
  // 로그인이 되어있다면 다음 미들웨어로
}

app.js

app.use('/posts', loginRequired ,postsRouter);