[게시판 만들기] TIL 6. JWT, Logout


  1. clearCookie

  2. JWT 토큰 검증 (jwt.verify())


😀 Notice Board 레파지토리😀
Stack : Javascript, express, nodejs, sequelize


★ : 이번 과정을 통해서 새로 만들어진 파일

☆ : 이번 과정을 통해서 수정된 파일

/
├── 📁/server
│   ├── 📄README.md                     # Notice_Board README.md 파일
│   ├── 📄index.js                      # node.js로 작성된 웹 서버 진입점
│   ├── 📄package.json
│   ├── 📄package-lock.json
│   ├── 📄.gitignore                    # node_modules, env 등 포함되지 않게 설정요망
│   ├── 📁/config                       # 환경변수
│   │    ├── 📄config.js        
│   │    └── 📄jwt.js                   # jwt 관련 환경변수
│   ├── 📁/controllers                  # 기능 API
│   │    ├── 📄index.js        
│   │    ├── 📁User                     # 유저관련 API
│   │         ├── 📄☆ index.js 
│   │         ├── 📄join.js             # join API
│   │         ├── 📄login.js            # login API
│   │         └── 📄★ logout.js        # logout API
│   ├── 📁/middlewares
│   │    ├── 📄CheckEmailForm.js        # email 양식이 맞는지 확인하는 middleware
│   │    ├── 📄CheckPassword.js         # password 조건이 맞는지 확인하는 middleware      
│   │    └── 📄★ checkToken.js         # ★ JWT 검증 middleware
│   ├── 📁/migrations 
│   │    ├── 📄20210819052749-create-users.js
│   │    └── 📄20210819065842-add-column-in-usersTable.js          
│   ├── 📁/models                       # DB 모델 파일
│   │    ├── 📄index.js
│   │    └── 📄users.js
│   ├── 📁/routes
│        └── 📄☆ index.js               # 분기 파일
│   


/controllers/Users/logout.js

const { Users } = require('../../models');

module.exports = {
  post : async (req, res) =>{
    res.clearCookie("sid");
    res.status(205).send("logged out success");
  }
}

/middlewares/checkToken.js

const jwt = require('jsonwebtoken');
let secretObj = require("../config/jwt");

const checkToken = (req, res, next) => {
  const cookie = req.headers.cookie;
  const secret = secretObj.secret;

  if (!cookie){
    res.status(401).json({
      success:false,
      message:'unvaild token'
    })
  }

  const token = cookie.split("=")[1];
  const check = new Promise((resolve, reject) =>{
    jwt.verify(token, secret, (err, decoded) => {
      if (err) reject(err);
      resolve(decoded);
    })
  })

  check.then(user => {
    req.user = user;
    next();
  }).catch(err => {
      res.status(401).json({
        success: false,
        message: err
      })
  })
}

module.exports = checkToken;

/routes/index.js

const express = require('express');
const router = express.Router();

const checkToken = require('../middlewares/checkToken');
const { userController, myPageController } = require('../controllers');


router.get('/', (req, res) => {
  res.send("Site access success");
});

router.post('/user/join', userController.join.post);
router.post('/user/login', userController.login.post);

router.use(checkToken);
router.post('/user/logout', userController.logout.post);

module.exports = router;


1. controllers

/controllers/Users/logout.js

const { Users } = require('../../models');

module.exports = {
  post : async (req, res) =>{
    res.clearCookie("sid");
    res.status(205).send("logged out success");
  }
}

cookieparser 또한 bodyparser와 더불어서 Express 내부에 빌트인 되어있기 때문에 모듈 사용 없이 res.cookie()res.clearCookie()를 사용할 수 있다. 어제 token을 cookie에 넣어주었기 때문에, 로그아웃이 이 기록을 없애야 한다. 때문에 지정한 쿠키를 clearCookie()를 이용했다. token을 지울 목적이기 때문에 어제 token이 들어갈 쿠키이름인 “sid”를 매개변수로 하였다.


2. JWT 토큰 검증

/middlewares/checkToken.js

const jwt = require('jsonwebtoken');
let secretObj = require("../config/jwt");

const checkToken = (req, res, next) => {
  const cookie = req.headers.cookie;
  const secret = secretObj.secret;

  if (!cookie){
    res.status(401).json({
      success:false,
      message:'unvaild token'
    })
  }

  const token = cookie.split("=")[1];
  const check = new Promise((resolve, reject) =>{
    jwt.verify(token, secret, (err, decoded) => {
      if (err) reject(err);
      resolve(decoded);
    })
  })

  check.then(user => {
    req.user = user;
    next();
  }).catch(err => {
      res.status(401).json({
        success: false,
        message: err
      })
  })
}

module.exports = checkToken;

jwt.verify()라는 함수를 사용해 검증한다. verify 함수는 인자로는 발급받은 token, 생성할 때 사용한 비밀키, [옵션]이 된다. 옵션은 있어도 그만, 없어도 그만이다. 검증하면 토큰의 구역 중 PayLoad가 Decoded된다. 이 프로젝트에서는 로그인된 user의 email 값을 불러온다.

check.then(user => {
  req.user = user;
  next();
}).catch(err => {
  res.status(401).json({
    success: false,
    message: err
  })
})

비밀키가 맞아 토큰검증에 성공하면 email이 req.user저장된다. 앞으로 이것이 중요한 역할을 하게 될 것이다. (req.user = user;)비밀키가 틀리거나, 만료 시간이 지나면 err가 발생해 catch로 넘어간다.

/routes/index.js

const express = require('express');
const router = express.Router();

const checkToken = require('../middlewares/checkToken');
const { userController, myPageController } = require('../controllers');


router.get('/', (req, res) => {
  res.send("Site access success");
});

router.post('/user/join', userController.join.post);
router.post('/user/login', userController.login.post);

router.use(checkToken);
router.post('/user/logout', userController.logout.post);

module.exports = router;

이제 이것을 적용하려면 토큰 인증을 미들웨어처럼 불러오고, router.use()의 인자로 넣어주면 된다. ( app.use()처럼) 그리고 이것은 인증이 필요한 라우터 위에 위치해야 한다. 그러므로 앞으로 라우터는 router.use(checkToken);를 기준으로 그 위에 작성된 라우터는 로그인하지 않아도 되는 기능(인증이 필요 없는 기능), 그 아래에 작성된 라우터는 로그인을 해야 하는 기능(인증이 필요한 기능)이 될 것이다.




© 2020. by RIVER

Powered by RIVER