공부하는 히욤이

따라하며 배우는 노드, 리액트 시리즈 - 기본 강의 #11-12 로그인 기능 with Bcrypt/토큰 생성 with jsonwebtoken 본문

Programming/React+Node

따라하며 배우는 노드, 리액트 시리즈 - 기본 강의 #11-12 로그인 기능 with Bcrypt/토큰 생성 with jsonwebtoken

히욤이 2021. 8. 14. 00:10

[user.js]

userSchema.pre('save', function( next ){
    var user = this;
    if(user.isModified('password')) {
        
        // 비밀번호 암호화
        bcrypt.genSalt(saltRounds, function(err, salt){
            if(err) {
                return next(err);
            };
            
            bcrypt.hash(user.password, salt, function (err, hash){
                if(err) {
                    return next(err);
                } else {
                    user.password = hash;
                }
                // 일이 다 끝나면 next 함수로 index의 save로 보내버림
                next();
            });
        });
    } else {
        next();
    }
});

user.js의 pre('save') 부분에 비밀번호가 수정되지 않았을때는 그냥 넘어갈 수 있도록 next() 하는 부분을 추가했다.

else{ next() } 부분이 없으면 index로 넘어가지 않고 계속 user.js의 pre('save') 부분에 머물러 있다고 한다.

 

 

 

[index.js]

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

// body parsers
// application/x-www-form-urlencoded
app.use(express.urlencoded({extended: true}))

// application/json
app.use(express.json())
app.use(cookieParser());

app.post('/login', (req, res) => {
    // 요청된 이메일이 데이터베이스에 있는지 찾는다.
    User.findOne( { email : req.body.email }, (err, user) => {
        // user가 없다면
        if(!user){
            return res.json({
                loginSuccess: false,
                message : '제공된 이메일에 해당하는 유저가 없습니다.'
            })
        }

        // user가 있다면
        // 요청된 이메일이 데이터베이스에 있다면 비밀번호가 맞는지 확인
        user.comparePassword(req.body.password, (err, isMatch) => {
            if (!isMatch) {
                return res.json({
                    loginSuccess: false,
                    message : '비밀번호가 틀렸습니다.'
                })
            } else{
                // 비밀번호까지 맞다면 토큰을 생성
                user.generateToken((err, user) => {
                    if (err) {
                        return res.status(400).send(err);
                    }
                    
                    // 토큰을 저장한다. 어디에? 쿠키나 로컬스토리지
                    res.cookie("x_auth", user.token)
                        .status(200)
                        .json({
                            loginSuccess : true,
                            user_Id : user._id
                        })
                })
            }
        });
    });
});

로그인 기능을 구현하기 위해 app.post('/login') 으로 로그인 라우팅을 해주고 요청된 이메일이 데이터 베이스에 있는지 찾아본다.

findOne은 mongodb에서 자체제공하는 함수이다.

해당하는 유저가 있으면 데이터베이스에 있는 비밀번호와 일치하는지 확인한다.

comparePassword는 비밀번호가 일치하는지 비교하기위한 함수이고 generateToken은 토큰을 생성하는 함수인데 User.js에서 자체적으로 만든 함수들이다.

 

 

[User.js]

userSchema.methods.comparePassword = function(plainPassword, callback){
    bcrypt.compare(plainPassword, this.password, function(err, isMatch){
        if(err) {
            return callback(err);
        } else {
            callback(null, isMatch);
        }
    });
}

plainPassword는 index의 req.body.password로 받아온 비밀번호이고 this.password는 데이터베이스에 저장된 비밀번호다.

plainPassword의 경우 12345와 같은 문자로 되어있지만 this.password는 암호화된 $2b$Z032$d$o2D와 같은 문자열로 되어있기 때문에 plainPassword를 암호화해서 비교해야 한다.

 

 

password까지 같다면 토큰을 생성한다.

토큰을 생성하기위해 jsonwebtoken 라이브러리를 설치한다.

C:\practices\react-node\boiler-plate> npm install jsonwebtoken

 

https://www.npmjs.com/package/jsonwebtoken

 

jsonwebtoken

JSON Web Token implementation (symmetric and asymmetric)

www.npmjs.com

자세한 사용법은 npmjs 사이트에 있어서 참고하면 될 것 이다.

 

 

 

[User.js]

const jwt = require('jsonwebtoken');

userSchema.methods.generateToken = function(callback){
    var user = this;
    // jsonwebtoken을 이용해서 token을 생성하기
    var token = jwt.sign(user._id.toHexString(), 'secretToken');
    user.token = token;
    user.save(function(err, user){
        if(err) {
            return callback(err);
        } else{
            callback(null, user);
        }
    });
};

token을 생성하기위해 jsonwebtoken을 require해주고 sing 함수로 token을 생성한다.

mongodb에서는 id가 _id로 되어있기 때문에 user._id라고 적는다.

생성된 토큰은 user에 넣어준다.

생성된 토큰은 index.js에서 쿠키에 넣어 보관한다.

 

cookie-parser는 express에서 요청된 쿠키를 쉽게 추출할 수 있도록 하는 라이브러리인데 토큰을 쿠키에 저장하기 위해 설치한다.

C:\practices\react-node\boiler-plate> npm install cookie-parser

 

생성된 토큰을 쿠키에 넣어주고 postman을 이용해 실행결과를 확인해본다.

로그인에 성공한 경우에는 다음과 같은 결과가 나오고

 

실해팼을 경우에는 loginSuccess: false와 message가 나온다.

(왼) 해당하는 이메일이 없을 경우 (오) 비밀번호가 틀렸을 경우