Node.js

API 서버에서 사용량 제한하기

쿠와와 2021. 1. 11. 12:29

1. 사용량 제한 구현과 이유 

 

DOS 공격 등을 대비해야 함 -> 새로고침을 계속하면서 공격하는 것 (무차별 요청)

  • 일정 시간동안 횟수 제한을 두어 무차별적인 요청을 막을 필요가 있음
  • npm i express-rate-limit
  • apiLimiter 미들웨어 추가
  • windowMS(기준 시간), max(허용 횟수), delayMS(호출 간격), handler(제한 초과 시 콜백 함수)
  • deprecated 미들웨어는 사용하면 안 되는 라우터에 붙여서 사용 시 경고
const jwt = require('jsonwebtoken');

exports.isLoggedIn = (req, res, next) => {
  if (req.isAuthenticated()) {
    next();
  } else {
    res.status(403).send('로그인 필요');
  }
};

exports.isNotLoggedIn = (req, res, next) => {
  if (!req.isAuthenticated()) {
    next();
  } else {
    res.redirect('/');
  }
};

exports.verifyToken = (req, res, next) => {
  try {
    req.decoded = jwt.verify(req.headers.authorization, process.env.JWT_SECRET);
    return next();
  } catch (error) {
    if (error.name === 'TokenExpiredError') { // 유효기간 초과
      return res.status(419).json({
        code: 419,
        message: '토큰이 만료되었습니다',
      });
    }
    return res.status(401).json({
      code: 401,
      message: '유효하지 않은 토큰입니다',
    });
  }
};

 

2. 응답 코드 정리

응답 코드를 정리해서 어떤 에러가 발생했는지 알려주기

  • 일관성이 있으면 됨

 

 

3. 새 라우터 버전 내놓기

사용량 제한 기능이 추가되어 기존 API와 호환되지 않음

  • 이런 경우 새로운 버전의 라우터를 내놓으면 됨
  • v2 라우터 작성(apiLimiter 추가됨)
  • v1 라우터는 deprecated 처리(router.use로 한 번에 모든 라우터에 적용)

nodecat의 버전 v2로 바꾸기

const express = require('express');
const jwt = require('jsonwebtoken');

const { verifyToken, apiLimiter } = require('./middlewares');
const { Domain, User, Post, Hashtag } = require('../models');

const router = express.Router();

router.post('/token', apiLimiter, async (req, res) => {
  const { clientSecret } = req.body;
  try {
    const domain = await Domain.findOne({
      where: { clientSecret },
      include: {
        model: User,
        attribute: ['nick', 'id'],
      },
    });
    if (!domain) {
      return res.status(401).json({
        code: 401,
        message: '등록되지 않은 도메인입니다. 먼저 도메인을 등록하세요',
      });
    }
    const token = jwt.sign({
      id: domain.User.id,
      nick: domain.User.nick,
    }, process.env.JWT_SECRET, {
      expiresIn: '30m', // 30분
      issuer: 'nodebird',
    });
    return res.json({
      code: 200,
      message: '토큰이 발급되었습니다',
      token,
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      code: 500,
      message: '서버 에러',
    });
  }
});

router.get('/test', verifyToken, apiLimiter, (req, res) => {
  res.json(req.decoded);
});

router.get('/posts/my', apiLimiter, verifyToken, (req, res) => {
  Post.findAll({ where: { userId: req.decoded.id } })
    .then((posts) => {
      console.log(posts);
      res.json({
        code: 200,
        payload: posts,
      });
    })
    .catch((error) => {
      console.error(error);
      return res.status(500).json({
        code: 500,
        message: '서버 에러',
      });
    });
});

router.get('/posts/hashtag/:title', verifyToken, apiLimiter, async (req, res) => {
  try {
    const hashtag = await Hashtag.findOne({ where: { title: req.params.title } });
    if (!hashtag) {
      return res.status(404).json({
        code: 404,
        message: '검색 결과가 없습니다',
      });
    }
    const posts = await hashtag.getPosts();
    return res.json({
      code: 200,
      payload: posts,
    });
  } catch (error) {
    console.error(error);
    return res.status(500).json({
      code: 500,
      message: '서버 에러',
    });
  }
});

module.exports = router;

v1 API를 사용하거나 사용량을 초과하면 에러 발생

'Node.js' 카테고리의 다른 글

Nodejs Test하는 이유와 Jest  (0) 2021.01.12
CORS 에러 이해하기  (0) 2021.01.11
Nodejs 호출 서버 만들기  (0) 2021.01.10
Node에서 API 서버 만들기  (0) 2021.01.08
multer 사용해서 이미지 업로드 구현  (0) 2021.01.06