인증 미들웨어 설정 및 적용

인증이 필요한 api로 접근시 토큰 정보가 있고 유효한지를 먼저 체크하는 미들웨어를 생성한다.

http 헤더에 authorization 키가 있는지 체크하고 있다면 토큰 정보가 Bearer 다음에 토큰 정보가 들어가 있으므로 Bearer 가 잇는지 체크하고 없다면 401 에러를 리턴한다.

토큰을 꺼내서 verify 함수를 사용해서 토큰이 유효한지 체크한다. 체크후에는 req에 userId와 roles 정보를 세팅하여 뒤에 나올 권한 미들웨어에 넘겨준다.

src/middleware/AuthMiddleware.ts

import {verify} from 'jsonwebtoken';

export class AuthMiddleware {
  static verifyToken = async (req, res, next) => {

    if (!req.headers["authorization"] || !req.headers["authorization"].startsWith("Bearer ")) {
      return res.status(401).send({
        message: "Unauthorized!"
      });
    }

    const token = req.headers["authorization"].substring(7);

    verify(token, process.env.secret, (err, decoded) => {
      console.log(err);
      if (err) {
        return res.status(401).send({
          message: "Unauthorized!"
        });
      }
      console.log(decoded);
      req.userId = decoded.jti;
      req.roles = decoded.roles;
      next();
    });
  }
}  

인증이 필요한 api에 인증 미들웨어를 적용한다.

로그인과 회원가입인 /auth에는 인증이 필요없고 /images에도 인증이 없게 설정한다. 앞에서 만든 admin 사이트에에 인증 미들웨어를 아래와 같이 설정한다.

src/router/index.ts

import {Router} from "express";
import image from "./image";
import auth from "./auth";
import admin from "./admin";
import {AuthMiddleware} from "../middleware/AuthMiddleware";
import board from "./board";
import comment from "./comment";

const routes = Router();

routes.use('/board', board);

routes.use('/image', image);

routes.use('/comment', comment);

routes.use('/auth', auth);

routes.use('/admin',  AuthMiddleware.verifyToken, admin);

export default routes;

게시판의 경우는 쓰기, 수정, 삭제 시에 인증이 필요하므로 아래와 같이 인증 미들웨어를 설정한다.

src/router/board.ts

import {Router} from "express";
import {BoardController} from "../controller/BoardController";
import {AuthMiddleware} from "../middleware/AuthMiddleware";

const routes = Router();
routes.post('', AuthMiddleware.verifyToken, BoardController.addBoard);
routes.get('/list',  BoardController.findAllBoard);
routes.get('/count', BoardController.countBoard);
// routes.get(/^\/(\d+)$/, BoardController.findOneBoard);
routes.get('/:id', BoardController.findOneBoard);
routes.put('', AuthMiddleware.verifyToken, BoardController.modifyBoard);
routes.delete('', AuthMiddleware.verifyToken, BoardController.removeBoard);

export default routes;

댓글의 경우도 쓰기, 수정, 삭제 시에 인증 미들웨어를 적용한다.

import {Router} from "express";
import {CommentController} from "../controller/CommentController";
import {AuthMiddleware} from "../middleware/AuthMiddleware";

const routes = Router();
routes.post('', AuthMiddleware.verifyToken, CommentController.addComment);
routes.get('/list', CommentController.findAllComment);
routes.get('', CommentController.findOneComment);
routes.put('', AuthMiddleware.verifyToken, CommentController.modifyComment);
routes.delete('', AuthMiddleware.verifyToken, CommentController.removeComment);

export default routes;

권한 미들웨어 적용

권한이 필요한 api에 인증 미들웨어를 적용하기 위해서 권한 미들웨어를 생성한다.

인증 미들웨어가 먼저 적용되고 인증 미들웨어에서 req에 userId와 roles가 설정되어서 들어오기 때문에,

/api/admin으로 들어올때 req.roles에 ROLE_ADMIN 이 있는지 체크하고 없으면 권한 없음 에러를 리턴한다.

src/middleware/AuthMiddleware.ts

export class AuthMiddleware {
...

  static hasRole = async (req, res, next) => {
    console.log(req.userId, req.roles);

    if (req.baseUrl.startsWith('/api/admin')) {
      if (req.roles.indexOf('ROLE_ADMIN') < 0) {
        return res.status(401).send({
          message: "Admin Role is needed!"
        });
      }
    } else if (req.url.startsWith('/api/moderator')) {
      if (req.roles.indexOf('ROLE_MODERATOR') < 0) {
        return res.status(401).send({
          message: "Moderator Role is needed!"
        });
      }
    }

    next();
  }
}

admin api에 권한 미들웨어를 적용한다.

src/router/index.ts

...

routes.use('/admin',  AuthMiddleware.verifyToken, AuthMiddleware.hasRole, admin);

export default routes;