[웹] Node.js & React 회원관리 - 4

또여·2021년 6월 23일
0

노드 리액트 강의

목록 보기
4/5

로그인/로그아웃 API 만들기

index.js

로그인할때 체크해야하는 요소 순서대로 작성하고, 체크하는 요소가 false일때, error 메세지를 잘 뿌리고, true면 진행하도록 함

  • 작성한 이메일이 DB에 있는지
  • 올바른 비밀번호인지
  • 로그인이 가능하면 Token 생성

1. 작성한 이메일이 DB에 있는지

User.findOne 은 기본 함수이고,
{email: req.body.email}는 postman 으로 보낼 때 body중에 key인 'email'의 값을 읽어오는 것임

const cookieParser = require('cookie-parser')

app.post('/api/users/login', (req, res) => {
    //요청된 이메일을 DB에 있는지 찾고
    User.findOne({ email: req.body.email }, (err, user) => {
        if(!user) {
            return res.json({
                loginSucess: false,
                message: "제공된 이메일에 해당하는 유저가 없습니다."
            })
        }        
        //있으면 비밀번호가 맞는건지 확인
        user.comparePassword(req.body.password, (err, isMatch) => {
            if(!isMatch)
            return res.json({
                loginSucess: false,
                message: "비밀번호가 잘못되었습니다."
            })
        })
        //같으면 Token 생성
        user.generateToken((err, user) => {
            if(err) return res.status(400).send(err);
            
            //생성한 토큰을 user에 넣었고, 어딘가에 저장해야함 쿠키나 로컬 스토리지
            res.cookie("x_auth", user.token)
            .status(200)
            .json({loginSuccess: true, userId: user._id})
        })
    })
})

2. 올바른 비밀번호를 입력한건지

user.comparePassword는 함수를 User.js에 작성
입력받은 password를 암호화하고, 기존에 암호화된 값을 비교해서 일치하면 ture를 리턴

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

3. 로그인이 가능하면 token생성

token을 생성해서 쿠키든 어디엔가 저장해두고, token이 일치할 때만, 승인된 페이지에 접속하는 용도로 쓸 수 있음

npm install jsonwebtoken --save

로 설치

https://www.npmjs.com/package/jsonwebtoken
사용방법은 해당 웹 문서에서

이런식으로 jwt를 먼저 require로 가져오고
token을 sign 함수로 생성해준다

User.js 에 아래와 같이 작성해주고

const jwt = require('jsonwebtoken');

userSchema.methods.generateToken = function(cb){
    var user = this;
    //json web token을 이용해서 토큰 생성
    var token = jwt.sign(user._id.toHexString(), 'secretToken')
    user.token = token
    user.save(function(err, user){
        if(err) return cb(err)
        cb(null, user)
    })

}

index.js에서 작성한 함수인 generateToken함수를 불러와 token을 생성해줌
생성한 토큰을 cookie에 저장해야하므로

npm install cookie-parser --save

해주고 index.js에서 해줬던 토큰생성하는 부분에

user.generateToken((err, user) => {
            if(err) return res.status(400).send(err);
            
            //생성한 토큰을 user에 넣었고, 어딘가에 저장해야함 쿠키나 로컬 스토리지
            res.cookie("x_auth", user.token)
            .status(200)
            .json({loginSuccess: true, userId: user._id})
        })

이렇게 해줄수 있게 된다

4. auth.js로 인증체크


어떻게 관리하든 자유지만 server > middleware 폴더 하위에 auth.js를 만들었다

auth.js 에서 작성하는건데,
'쿠키에 저장한 토큰'과 'User 모듈'을 비교하는것임

User.findByToken 함수로 쿠키에 저장된 토큰 넣고 비교해서 에러체크

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

let auth = (req, res, next) => {
    //인증처리를 하는곳
    //DB에서 가지고있는 토큰과 캐시에서 가지고 있는 토큰을 비교

    //클라이언트 쿠키에서 토큰을 가져옴
    let token = req.cookies.x_auth;
    
    //토큰 복호화한 후, 유저 찾기
    User.findByToken(token, (err, user) => {
        if(err) throw err;
        if(!user) return res.json({isAuth: false, error: true});

        req.token = token;
        req.user = user;
        next();
    })
}

module.exports = { auth };

User.findByToken 함수는 User.js쪽에서 Static으로 만들어준다

User.js

userSchema.statics.findByToken = function(token, cb){
    var user = this;

    //가져온 토큰을 decode => User Id가 나옴
    jwt.verify(token, 'secretToken', function(err, decoded){
        user.findOne({"_id": decoded, "token":token}, function(err, user){
            if(err) return cb(err);
            cb(null, user)
        })
    })

}

jwt.verify() 의 두번째 인자는 처음 토큰 생성할 때와 동일한 것으로 해줘야한다
토큰을 decode해서 나오는 값은 user ID가 나오게 되므로, 이 값으로 findOne()함수로 인자를 찾아내는 로직임


index.js에 auth 호출하는 GET API 작성해서 성공, 실패하여 true 띄워줌

app.get('/api/users/auth', auth, (req, res) => {
    res.status(200).json({
        _id: req.user._id,
        isAdmin: req.user.role === 0 ? false : true,
        isAuth:true,
        email:req.user.email,
        lastname:req.user.lastname,
        role:req.user.role,
        image:req.user.image
    })
})

isAuth와 isAdmin 등 값을 넣어주어 client 단에서 화면 접근할 때 체크하도록 쓰임

5. 로그아웃 API

로그인보다는 매우 간단하게 가능
당연히 GET API이며, auth를 해주는 이유는 로그아웃을 하려면 로그인이 되어있어야 하니까!
findOneAndUpdate 함수는 기본함수로 id로 찾아서 token를 빈 문자열로 초기화함으로써 해당 id의 토큰을 날려버린다

app.get('/api/users/logout', auth, (req, res) => {
    User.findOneAndUpdate({_id: req.user._id}
        , {token: ""}
        , (err, user) => {
            if(err) return res.json({success: false, err})
            return res.status(200).send({
                success: true
            })
    })
})

6. 기능 확인

로그아웃 API호출하면 token이 초기화 되는지를 확인해본다

<로그아웃 API 호출 전>

<로그아웃 API 호출 후>

token이 초기화 된것을 봐주면 되겠다

profile
기록 열심히하는 개발자인척

0개의 댓글