• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    해결됨

10.6 재질문…!

23.06.27 20:00 작성 23.06.27 20:06 수정 조회수 455

0

exports.apiLimiter = async (req, res, next) => {
  let user;
  if (res.locals.decoded) {
    user = await User.findOne({ where: { id: res.locals.decoded.id } });
  }
  rateLimit({
    widowMs: 60 * 1000,
    max: user?.type === "premium" ? 10 : 1,
    handler(req, res) {
      res.status(this.statusCode).json({
        code: this.statusCode,
        message: "1분에  열 번만 요청 할 수 있습니다...",
      });
    },
  })(req, res, next);
};

 

Api 프리미엄고객만 1분에 열번만요청할수있게 미들웨어 확장패턴으로

만들었습니다 그런데 localhost:4000/myposts 접속해서 10 번이상새로고침해도 api가 제한이 안되고 제가작성한 게시글 목록만 뜹니다

답변 1

답변을 작성해보세요.

0

이 부분 apiLimiter가 호출될 때마다 rateLimit 미들웨어가 새로 생성되서 그렇습니다. 다음과 같이 수정해야할 것 같네요.

const limiter = rateLimit({
  widowMs: 60 * 1000,
  max: (req, res) => {
    if (req.user?.type === 'premium') { return 10 }
    return 1;
  },
  handler(req, res) {
    res.status(this.statusCode).json({
      code: this.statusCode,
      message: `1분에 ${req.user?.type === 'premium' ? '열' : '한'} 번만 요청 할 수 있습니다...`,
    });
  },
});

exports.apiLimiter = async (req, res, next) => {
  let user;
  if (res.locals.decoded) {
    user = await User.findOne({ where: { id: res.locals.decoded.id } });
  }
  req.user = user;
  limiter(req, res, next);
};

 

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.27

선생님 감사합니다^^

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

선생님 프리미엄 클라이언트 시크릿키발급하고 실행했는데 1분에 10번이아니고 1번만 요청할수있다고 뜹니다 혹시다른부분에서 문제가 생겨서 그런걸까요? ㅠㅜ

handler 안에서 console.log(req.user) 해보세요

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

Executing (default): SELECT `id`, `host`, `type`, `clientSecret`, `createdAt`, `updatedAt`, `deletedAt`, `UserId` FROM `domains` AS `Domain` WHERE (`Domain`.`deletedAt` IS NULL AND `Domain`.`host` = 'localhost:4000') LIMIT 1;
Executing (default): SELECT `Domain`.`id`, `Domain`.`host`, `Domain`.`type`, `Domain`.`clientSecret`, `Domain`.`createdAt`, `Domain`.`updatedAt`, `Domain`.`deletedAt`, `Domain`.`UserId`, `User`.`id` AS `User.id`, `User`.`email` AS `User.email`, `User`.`nick` AS `User.nick`, `User`.`password` AS `User.password`, `User`.`provider` AS `User.provider`, `User`.`snsId` AS `User.snsId`, `User`.`createdAt` AS `User.createdAt`, `User`.`updatedAt` AS `User.updatedAt`, `User`.`deletedAt` AS `User.deletedAt` FROM `domains` AS `Domain` LEFT OUTER JOIN `Users` AS `User` ON `Domain`.`UserId` = `User`.`id` AND (`User`.`deletedAt` IS NULL) WHERE (`Domain`.`deletedAt` IS NULL AND `Domain`.`clientSecret` = '3b7b5e45-c56f-480f-9f3b-6d59afd800b6') LIMIT 1;
POST /v2/token 200 165.745 ms - 253
Executing (default): SELECT `id`, `host`, `type`, `clientSecret`, `createdAt`, `updatedAt`, `deletedAt`, `UserId` FROM `domains` AS `Domain` WHERE (`Domain`.`deletedAt` IS NULL AND `Domain`.`host` = 'localhost:4000') LIMIT 1;
Executing (default): SELECT `id`, `email`, `nick`, `password`, `provider`, `snsId`, `createdAt`, `updatedAt`, `deletedAt` FROM `Users` AS `User` WHERE (`User`.`deletedAt` IS NULL AND `User`.`id` = 2);

노드버드 api 콘솔에 select domain 맨끝에 보시면 clientSecret` = '3b7b5e45-c56f-480f-9f3b-6d59afd800b6' 프리미엄 클라이언트 키 발급한거 나오고

GET /myposts 304 378.750 ms - -

노드캣 콘솔에는 이렇게 나오는데

handler 안에서 console.log(req.user)했는데도 똑같습니다 답변감사합니다!!!

console.log(req.user) 는 단순히 콘솔에 찍는거고 아무것도 안 바꾸니 당연히 똑같죠.

req.user값이 안 나오나요?

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

localhost:4000/myposts 접속해서 새로고침하면 nodebird-api콘솔에 Req.user 값 나옵니다

console.log(req.rateLimit) 도 찍어서 요청 보낼 때마다 limit을 위한 숫자 올라가는지 확인해봐야할 것 같습니다.

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

console.log(req.rateLimit) 도 찍었는데 limit숫자가 1로 그대로입니다

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

const limiter = rateLimit({
  widowMs: 60 * 1000,
  max: async(req, res) => {
    if (await req.user?.type === 'premium') { return 10 }
    return 1;
  },
  handler(req, res) {
console.log(req.user)
console.log(req.rateLimit) 
res.status(this.statusCode).json({
      code: this.statusCode,
      message: `1분에 ${req.user?.type === 'premium' ? '열' : '한'} 번만 요청 할 수 있습니다...`,
    });
  },
});

exports.apiLimiter = async (req, res, next) => {
  let user;
  if (res.locals.decoded) {
    user = await User.findOne({ where: { id: res.locals.decoded.id } });
  }
  req.user = user;
  limiter(req, res, next);
};

급행 속도 제한 - npm (npmjs.com)

여기 공식문서에 혹시나해서 max부분에 async await 해봤는데 똑같습니다

router.get('/posts/my', apiLimiter, verifyToken, getMyPosts);

이거 verifyToken이 더 뒤에 있어서 apiLimit에 req.user가 없네요.

Hongsun1님의 프로필

Hongsun1

질문자

2023.06.29

감사합니다 제가 밖에있어서 이따 실행하고 말씀드릴게요 ^^

const limiter = rateLimit({
  widowMs: 60 * 1000,
  max: (req, res) => {
    if (req.user?.Domains[0]?.type === 'premium') { return 10 }
    return 1;
  },
  handler(req, res) {
    res.status(this.statusCode).json({
      code: this.statusCode,
      message: `1분에 ${req.user?.Domains[0]?.type === 'premium' ? '열' : '한'} 번만 요청 할 수 있습니다...`,
    });
  },
});

exports.apiLimiter = async (req, res, next) => {
  let user;
  if (res.locals.decoded) {
    user = await User.findOne({ where: { id: res.locals.decoded.id }, include: { model: Domain } });
  }
  req.user = user;
  limiter(req, res, next);
};
Hongsun1님의 프로필

Hongsun1

질문자

2023.06.30

선생님 코드 감사합니다 아쉽게도 많이 배워야할것같아서 ㅠㅜ 혹시 제가 빠트린 코드가 있을까요 ? 아니면 다른부분 확인할 사항이 있을까요? 작성한 코드 올려드립니다

nodebird-api -> middlewares-> index.js

const jwt = require("jsonwebtoken"); //토큰을 검사하는 미들웨어
const rateLimit = require("express-rate-limit");
const User = require("../models/user");
const { Domain } = require("../models/");
const cors = require("cors");

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

exports.isNotLoggedIn = (req, res, next) => {
  if (!req.isAuthenticated()) {
    // 패스포트 통해서 로그인 안했으면
    next();
  } else {
    const message = encodeURIComponent("로그인한 상태입니다.");
    res.redirect(`/?error=${message}`); //localhost:8001? error=메시지
  }
};

//토근검사
exports.verifyToken = (req, res, next) => {
  try {
    res.locals.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: "유효하지 않은 토큰입니다.",
    });
  }
};

const limiter = rateLimit({
  widowMs: 60 * 1000,
  max: (req, res) => {
    if (req.user?.Domains[0]?.type === "premium") {
      return 10;
    }
    return 1;
  },
  handler(req, res) {
    res.status(this.statusCode).json({
      code: this.statusCode,
      message: `1분에 ${
        req.user?.Domains[0]?.type === "premium" ? "열" : "한"
      } 번만 요청 할 수 있습니다...`,
    });
  },
});

exports.apiLimiter = async (req, res, next) => {
  let user;
  if (res.locals.decoded) {
    user = await User.findOne({
      where: { id: res.locals.decoded.id },
      include: { model: Domain },
    });
  }
  req.user = user;
  limiter(req, res, next);
};

exports.deprecated = (req, res) => {
  res.status(410).json({
    code: 410,
    message: "새로운 버전이 나왔습니다. 새로운 버전을 사용하세요",
  });
};

exports.corsWhenDomainMatches = async (req, res, next) => {
  const domain = await Domain.findOne({
    where: { host: new URL(req.get("origin")).host },
  });
  if (domain) {
    cors({
      origin: true,
      Credential: true,
    })(req, res, next); //미들웨어 확장패턴
  } else {
    next();
  }
};

 

nodebird-api -> routes -> v2.js

const express = require("express");
const {
  verifyToken,
  apiLimiter,
  corsWhenDomainMatches,
} = require("../middlewares");
const {
  createToken,
  tokenTest,
  getMyPosts,
  getPostsByHashtag,
} = require("../controllers/v2");
const cors = require("cors");

const router = express.Router();

router.use(corsWhenDomainMatches);

router.use(
  cors({
    origin: true, 
    credentials: true, //쿠키요청
  })
);

router.post("/token", apiLimiter, createToken);


router.get("/test", verifyToken, apiLimiter, tokenTest);


router.get("/posts/my", verifyToken, apiLimiter, getMyPosts);

// GET /v2/posts/hashtag/:title
router.get("/posts/hashtag/:title", verifyToken, apiLimiter, getPostsByHashtag);

module.exports = router;