저번 스터디 때 Postman을 이용하여 회원가입을 하고 그 결과 MongoDB에 다음과 같이 데이터가 저장되어 있는 것을 확인할 수 있었다.

MongoDB에 저장된 유저 데이터

회원가입 시 입력했던 name, email, password가 잘 저장되어있다.

하지만 password가 너무 적나라하게 드러나있어 보안에 취약하므로 암호화하여 저장할 필요가 있다.

 

Bcrypt

password를 암호화하기 위해서는 먼저 Bcrypt를 설치해야한다.

암호화는 회원가입 라우터에서 유저 정보를 저장하기 전에 실행이 되야하므로, mongoose에서 제공하는 pre라는 메소드를 사용하여 암호화 코드를 작성한다.

 

↓Bcrypt 참고 사이트

www.npmjs.com/package/bcrypt

 

// User.js

const bcrypt = require('bcrypt');

const saltRounds = 10;   //10자리 salt


userSchema.pre('save', function( next ){  //user 정보를 저장하기 전에 실행

    var user = this; // = userSchema

    if(user.isModified('password')){  //password 변경시에만
    
        //비밀번호 암호화
        
        bcrypt.genSalt(saltRounds, function(err, salt) {  //salt 생성
        
            if(err) return next(err)
            

            bcrypt.hash(user.password, salt, function(err, hash) {
            
                if(err) return next(err)
                
                user.password = hash  // hash된 비밀번호를 user model-password에 저장
                
                next()
            })
        })
        
    } else {
    
        next()
        
    }
}) 

암호화가 완료되면 next(), error가 발생하면 next(err)를 통해 회원가입 라우터에 있는 user.save를 실행시켜준다.

 

이번에도 Postman을 이용하여 회원가입을 해보면, 다음과 같이 암호화된 비밀번호가 저장된 것을 확인할 수 있다.

암호화된 비밀번호

 

로그인 라우터

먼저 User.findOne()을 통해 로그인 요청된 이메일을 데이터베이스에서 찾는다.

해당 이메일이 있다면 Bcrypt를 이용해 입력한 비밀번호와 암호화된 비밀번호가 같은지 확인한다.

비밀번호가 같다면 토큰을 생성한다. 이때 토큰을 생성하기 위해서는 jsonwebtoken을 설치해야한다.

그리고 토큰을 쿠키에 저장하기 위해서 cookie-parser도 설치해야한다.

 

↓토큰 생성 참고 사이트

www.npmjs.com/package/jsonwebtoken

 

// index.js
//로그인 라우터

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

app.use(cookieParser());


router.post("/login", (req, res) => {

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



        //요청된 이메일이 데이터베이스에 있다면 비밀번호가 맞는지 확인
        
        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);

                //token을 쿠키로 저장
                
                res.cookie("x_authExp", user.tokenExp);
                
                
                res
                    .cookie("x_auth", user.token)
                    
                    .status(200)
                    
                    .json({
                    
                        loginSuccess: true, userId: user._id
                        
                    });
            });
        });
    });
});
// User.js

const jwt = require('jsonwebtoken');


userSchema.methods.comparePassword = function(plainPassword, cb) {

    //plainPassword 1234567와 암호화된 비밀번호가 같은지 확인. plainPassword를 암호화하여 확인.
    
    bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
    
        if(err) return cb(err);
        
            cb(null, isMatch)
    }) 
}


userSchema.methods.generateToken = function(cb) {

    var user = this;

    //jsonwebToken을 이용해서 token을 생성
    
    var token = jwt.sign(user._id.toHexString(), 'secretToken')
    
    var oneHour = moment().add(1, 'hour').valueOf();
    
    //user._id + 'secretToken' = token
    //나중에 'secretToken'으로 user._id를 알아낼 수 있다
    
    user.token = token
    
    
    user.save(function(err, user) {
    
        if(err) return cb(err)
        
        cb(null, user)
    })
}

userSchema.methods.comparePassword에서 plainPassword는 입력받은 비밀번호이고, this.password는 userSchema에 저장되어 있는 암호화된 비밀번호이다.

Postman으로 로그인 결과 확인

 

 

Auth 라우터

페이지마다 또는 기능마다 접근권한이 다를 수 있기 때문에 사용자의 권한을 체크하는 라우터가 필요하다.

따라서 로그인 시에 생성된 토큰이 저장된 쿠키를 통해 로그인 인증을 해준다.

// User.js

userSchema.statics.findByToken = function(token, cb) {

    var user = this;


    //token을 decode함
    jwt.verify(token, 'secretToken', function(err, decoded) {
    
        //user _id를 이용해서 user를 찾은 다음에 
        //클라이언트에서 가져온 token과 DB에 보관된 token이 일치하는지 확인
        user.findOne({ "_id": decoded, "token": token }, function(err, user) {
        
            if(err) return cb(err);
            
            cb(null, user)
        })
    })
}

 

// auth.js

const { User } = require("../models/User");


//인증처리 하는 곳
let auth = (req, res, next) => {
   
    //client 쿠키에서 token을 가져옴
    let token = req.cookies.x_auth;

    //token을 복호화한 후 user를 찾음
    User.findByToken(token, (err, user) => {

        if(err) throw err;
        
        //user가 없으면 인증 실패
        if(!user) return res.json({ isAuth: false, error: true })

        //user가 있으면 인증 완료
        req.token = token;
        
        req.user = user;
        
        next();
    })
}

module.exports = { auth };

여기서 req.tokenreq.user에 각각 token과 user를 넣어줌으로써 Auth 라우터에서 그 정보를 사용할 수 있다.

 

// index.js
//Auth 라우터

const { auth } = require("../middleware/auth");


router.get("/auth", auth, (req, res) => {

//여기까지 미들웨어(auth)를 통과했다는 얘기는 Authentication이 True 라는 의미

    res.status(200).json({
    
        _id: req.user._id, //auth.js에서 user를 req.user에 넣어줘서 가능
        
        isAdmin: req.user.role === 0 ? false : true, //role 0이면 일반유저, 0이 아니면 관리자
        
        isAuth: true,
        
        email: req.user.email,
        
        name: req.user.name,
        
        lastname: req.user.lastname,
        
        role: req.user.role,
        
        image: req.user.image,
    });
});

 

 

로그아웃 라우터

로그아웃 라우터에서는 로그아웃 하려는 유저를 데이터베이스에서 찾아서 그 유저의 토큰을 지워주는 기능을 한다.

토큰을 지워주는 이유는 위에서 진행된 auth 라우터에서 토큰 값을 가지고 유저가 로그인 상태인지 확인하기 때문이다.

// index.js
//로그아웃 라우터

router.get("/logout", auth, (req, res) => {

    User.findOneAndUpdate({ _id: req.user._id },   //_id로 유저를 찾고, 저장된 데이터를 업데이트 시켜줌
    
        { token: "", tokenExp: "" }, 
        
        (err, doc) => {
        
        if (err) return res.json({ success: false, err });
        
        return res.status(200).send({
        
            success: true
        });
    });
});

 

위의 로그인 라우터 파트에서 Postman을 이용해 로그인한 결과 MongoDB를 확인해보면 다음과 같이 토큰이 생성된 것을 확인할 수 있다.

MongoDB에 저장된 토큰

 

이제 Postman을 통해 로그아웃을 해보면 다음과 같이 success: true가 나온다. 

Postman으로 로그아웃 실행

 

로그아웃 후 MongoDB에서 지워진 토큰

 

이렇게 로그아웃 기능까지 완성되었다.

 

 

'Study > React & Node.js' 카테고리의 다른 글

노드 리액트 기초 - 6th Day  (0) 2021.07.20
노드 리액트 기초 - 5th Day  (0) 2021.04.02
노드 리액트 기초 - 4th Day  (2) 2021.04.02
노드 리액트 기초 - 2nd Day  (0) 2021.04.01
노드 리액트 기초 - 1st Day  (0) 2021.04.01

+ Recent posts