<목차>
Bcrypt로 비밀번호 암호화 하기
로그인 기능 with Bcrypt
토큰 생성 with jsonwebtoken

Bcrypt로 비밀번호 암호화 하기

현재 DB에 저장된 비밀번호의 데이터 저장 형태를 보면 전혀 안전하지 않습니다.
그래서 Bcrypt를 이용해 비밀번호를 암호화 해서 DB에 저장해줘야할 필요성이 있습니다.

npm install bcrypt --save 

Bycrpt로 비밀번호를 암호화 해보겠습니다.

일단 Register Route로 가서 거기서 코드를 추가하거나 수정합니다.
언제 유저 정보(Account, Password 등)를 암호화 할 지 타이밍이 중요한데, 보통은 정보들을 DB에 저장하기 전에 암호화합니다.

자세한건 bcrypt 사이트를 보면서 진행하겠습니다.
아래는 해당 사이트에 적힌 코드를 가져와 적당히 수정한 index.js 내의 코드입니다.

const bcrypt = require('bcrypt');
const saltRounds = 10; //salt가 몇 글자인지

//정보를 저장하기전에 수행하는 암호화 과정
userSchema.pre('save', function( next ){
    var user = this; //바로 위의 userSchema를 가리킴.

    //비밀번호를 바꿀 때에만 암호를 걸어줘야함. 
    //다른 정보 변경시에는 그대로여야함. -> 그냥 next()
    if(user.isModified('password')){

    	//비밀번호를 암호화 시킨다.
        bcrypt.genSalt(saltRounds, function(err, salt) {

            if(err) return next(err)  //에러 발생 시 index.js의 user.save()로 보낸다.
            
            bcrypt.hash(user.password, salt, function(err, hash) { 
            	//user.password는 postman에서 사용한 pw를 의미한다.
                
                if(err) return next(err) //오류가 있는 경우
                user.password = hash //오류가 없을 경우 비밀번호를 해시된 것으로 바꿔줌
                next() 
                      
            })
        })
    } else { //비밀번호를 바꾸는게 아닌 다른걸 바꿀 때엔 바로 user.save()로 보낸다.
        next()
    }
   
})

암호화된 패스워드

위의 사진(DB에 저장된 user 목록)을 보면 password가 암호화된 것을 알 수 있습니다.
(암호화 전에 넣은 것은 password 지정을 안해주어서 password가 없는 채로 저장이 되었습니다.ㅠㅠ)

로그인 기능 with Bcrypt

로그인 기능을 구현하려면 다음과 같은 절차가 필요합니다.

  1. 데이터베이스에서 요청한 e-mail 찾기
  2. 데이터베이스에서 요청한 e-mail이 있다면 비밀번호가 같은지 확인
  3. 비밀번호까지 같다면 Token을 생성 (jsonwebtoken을 이용)

이 항목에서는 1,2를 설명할 것입니다.

먼저 로그인 route를 만듭니다. index.js를 봅시다.
app.post('/login',(req, res) => {}가 로그인 route입니다.
(이 route 안에 저 위의 3가지 기능을 넣는 것 입니다.)

<데이터 베이스에서 요청한 e-mail 찾기>
여기서는 findOne이라는 mongoDB가 제공하는 메소드를 사용합니다.

app.post('/login',(req, res) => {
  
  //요청한 이메일을 DB에 있는지 찾는다.
  User.findOne({ email: req.body.email }, (err, user) => {
    if(!user) { //user가 없는 상황이라면 아래를 리턴
      return res.json({
        loginSuccess: false,
        message: "제공된 이메일에 해당하는 유저가 없습니다."
      })
    }
  })
})

이걸로 해당 이메일이 없을 때의 코드는 완성했습니다.
해당 이메일이 있다면 다음단계로 넘어가야 합니다.

<데이터베이스에서 요청한 e-mail이 있다면 비밀번호가 같은지 확인>
비밀번호를 비교하기 위해 comparePassword라는 메소드를 만들었습니다.
여기서 req.body.password는 postman을 통해 로그인할 때 사용하는 비밀번호를 의미합니다.
비밀번호가 맞는지 안맞는지 확인하기 위해 User.js에서 isMatch라는 메소드를 만들고, 입력한 비밀번호(plainPassword)와 DB내에 저장된 비밀번호(this.password)를 비교합니다.

app.post('/login',(req, res) => {
  
  //요청한 이메일을 DB에 있는지 찾는다.
  User.findOne({ email: req.body.email }, (err, user) => {
    if(!user) { //user가 없는 상황이라면 아래를 리턴
      //생략
    }
    //요청된 이메일이 DB에 있다면 비밀번호가 맞는지 확인
    user.comparePassword(req.body.password, (err, isMatch) => {
      if(!isMatch) return res.json({ loginSuccess: false, message:"비밀번호가 틀렸습니다."})    
  })
})

아래는 User.js에 추가한 메소드입니다.


userSchema.methods.comparePassword = function(plainPassword, cb) {
    //plainPassword(1234567)과 DB에 있는 암호화된 비밀번호를 비교해야함.
    //그러므로 plainPassword를 암호화해서 DB의 번호(this.password)과 비교해야한다.
    bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
        if(err) return cb(err); //같지 않음. 에러 발생
        cb(null, isMatch); //같음. index.js의 user.comparePassword로 이동
    })
}

여기서도 이메일은 있으나 비밀번호가 맞지 않는 경우만을 코드로 작성하였습니다.
비밀번호가 맞는 경우에는 토큰을 생성하는 것으로 넘어가야 하기 때문입니다.

토큰 생성 with jsonwebtoken

이 부분은 해당 사이트를 참고하여 작성하였습니다.

앞서 이메일과 비밀번호가 모두 DB 내의 정보와 맞다는 가정 하에 토큰을 생성하게 됩니다.
그러면 로그인 route에 내용을 이어서 적어보도록 하겠습니다.

여기서 하나 추가하자면 cookie-parser라는걸 설치할 예정인데
이는 토큰을 저장할 저장소로 쿠키를 사용하기 위해서 입니다.

const cookieParser = require('cookie-parser');

app.post('/login',(req, res) => {
  
  //요청한 이메일을 DB에 있는지 찾는다.
  User.findOne({ email: req.body.email }, (err,user) => {
    if(!user) { //user가 없는 상황
    		//생략
    }
    
    //요청된 이메일이 DB에 있다면 비밀번호가 맞는지 확인
    user.comparePassword(req.body.password, (err, isMatch) => {
      if(!isMatch) return res.json({ loginSuccess: false, message:"비밀번호가 틀렸습니다."}) 
      
      //비밀번호까지 맞다면 토큰을 생성하기
      user.generateToken((err, user) => {
        if(err) return res.status(400).send(err);

        //위의 user에 이미 토큰이 있다. 이 토큰을 저장한다. 어디에?
        //쿠키 or 로컬 스토리지 (여기에선 쿠키에) -> 쿠키 파서를 설치
        res.cookie("x_auth", user.token) //쿠키 이름이 x_auth
        .status(200) //쿠키에 저장 성공
        .json({ loginSuccess: true, userId: user._id })      

      })
    })
  })
})

아래는 User.js의 파일에 추가한 내용입니다.
먼저 jsonwebtoken을 사용하기 위해 상수 jwt를 정의합니다.
위의 웹사이트를 보면 알 수 있지만 웹토큰은 sign을 이용해 만듭니다.

const jwt = require('jsonwebtoken');

userSchema.methods.generateToken = function(cb) {
    var user = this //위의 유저 정보를 끌어오기 위해 사용
    
    //jsonwebtoken을 이용해서 토큰을 생성
    var token = jwt.sign(user._id.toHexString(), 'secretToken') //유저의 아이디를 넣어 토큰을 생성

    // user._id + 'secretToken' = token (토큰을 만드는 식)
    // 나중에 token 해석 시 'secretToken'을 넣으면 user._id가 나온다.

    user.token = token //토큰을 유저스키마의 토큰에 넣는다
    user.save(function(err, user) { //토큰을 저장하고 에러 없으면 유저 정보를 전달
        if(err) return cb(err);
        cb(null, user);
    })
}

코드를 모두 적었다면 잘 작동하는지 Postman을 이용해 확인해봅니다.
이메일과 비밀번호를 적어주었습니다.
이 이메일과 비밀번호는 기존에 회원가입한 것과 동일하므로 로그인에 성공합니다.
로그인 성공

0개의 댓글

Powered by GraphCDN, the GraphQL CDN