본문 바로가기
프로그래밍/보안

IM-SPRINT-AUTH-TOKEN, JWT를 이용한 프로그램 구현. (server-token)

by 공부하는EJ 2022. 6. 8.
728x90

스프린트를 진행하다보니 전체적인 구성을 이해하기 어려워서 한 줄 한 줄 정리해봄.

 

💡 server.token/index.js

// 환경 변수 사용하기, import dotenv from dotenv
require("dotenv").config();
// fs 모듈은 파일 시스템에 접근하는 모듈이다. 파일을 생성하거나 삭제하고 읽거나 쓸 수 있다.
const fs = require("fs");
// https 모듈 사용
const https = require("https");
// cors 모듈 사용
const cors = require("cors");
// 요청된 쿠키를 쉽게 추출할 수 있도록 도와주는 미들웨어인 cookie-parser 사용
const cookieParser = require("cookie-parser");

//express 사용
const express = require("express");
const app = express();

//사용자의 요청에 반응하는 컨트롤러 사용
const controllers = require("./controllers");

// express 4.16.0 버전에서는 express generator에 body-parser가 내장되어 있어 따로 설치하지 않아도 아래와 같은 방식을 사용 가능.
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

// CORS 설정,
// origin: 도메인
// credentials: true로 설정해주어야 인증서가 동반이 돼 https 환경에서 통신이 가능하다.
// method: 요청에 사용될 메소드를 지정해주는 역할을 한다.
app.use(
    cors({
        origin: ["https://localhost:3000"],
        credentials: true,
        methods: ["GET", "POST", "OPTIONS"],
    })
);

// 쿠키파서를 미들웨어에 추가
app.use(cookieParser());

//라우팅 설정

// /lonin 라우터에 대한 PUT 요청에 응답.
app.post("/login", controllers.login);
// /accesstokenrequest 라우터에 대한 GET 요청에 응답.
app.get("/accesstokenrequest", controllers.accessTokenRequest);
// /refreshtokenrequest 라우터에 대한 GET 요청에 응답.
app.get("/refreshtokenrequest", controllers.refreshTokenRequest);

// HTTPS_PORT -> 환경변수 안의 port가 아니면 4000번대 포트 이용.
const HTTPS_PORT = process.env.HTTPS_PORT || 4000;

// 인증서 파일들이 존재하는 경우에만 https 프로토콜을 사용하는 서버를 실행합니다.
// 만약 인증서 파일이 존재하지 않는경우, http 프로토콜을 사용하는 서버를 실행합니다.
// 파일 존재여부를 확인하는 폴더는 서버 폴더의 package.json이 위치한 곳입니다.
let server;
if (fs.existsSync("./key.pem") && fs.existsSync("./cert.pem")) {
    const privateKey = fs.readFileSync(__dirname + "/key.pem", "utf8");
    const certificate = fs.readFileSync(__dirname + "/cert.pem", "utf8");
    const credentials = { key: privateKey, cert: certificate };

    server = https.createServer(credentials, app);
    server.listen(HTTPS_PORT, () => console.log("server runnning"));
} else {
    server = app.listen(HTTPS_PORT);
}
module.exports = server;

 

 

💡 server.token/controllers/users/login.js

아직 에러코드가 헷갈린다.

 

200 -> 성공

400 -> 잘못된 요청. 서버가 요청의 구문을 인식하지 못함.

401 -> 권한 없음. 인증 안 됨. 인증 실패

403 -> 금지됨. 서버가 요청을 거부하고 있음. 사용자가 리소스에 대한 필요 권한을 갖고 있지 않다. 인가 실패!

 

더 많은 코드 들이 있지만, 다 외울 수 없기에 필요하면 이 링크 참고! https://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C

 

HTTP 상태 코드 - 위키백과, 우리 모두의 백과사전

아래는 HTTP(하이퍼텍스트 전송 프로토콜) 응답 상태 코드의 목록이다. IANA가 현재 공식 HTTP 상태 코드 레지스트리를 관리하고 있다. 모든 HTTP 응답 코드는 5개의 클래스(분류)로 구분된다. 상태 코

ko.wikipedia.org

const { Users } = require("../../models");
//json web token 라이브러리를 사용해 토큰 생성.
const jwt = require("jsonwebtoken");

module.exports = async (req, res) => {
    // TODO: urclass의 가이드를 참고하여 POST /login 구현에 필요한 로직을 작성하세요.
    console.log(req.body.userId);

    // 기본 구성은 이전 sprint인 im-sprint-auth-session 에서 가져옴.
    // finidOne 함수를 이용해 id와 비밀번호 가져옴.
    const userInfo = await Users.findOne({
        where: { userId: req.body.userId, password: req.body.password },
    });

    //userinfo가 없을때
    if (!userInfo) {
        //404 에러 보내줌.
        res.status(404).json({ data: null, message: "not authorized" });
    }
    // userinfo가 있을 때
    else {
        // payload 구성.
        const payload = {
            id: userInfo.id,
            userId: userInfo.userId,
            email: userInfo.email,
            createdAt: userInfo.createdAt,
            updatedAt: userInfo.updatedAt,
        };

        // jsonwebtoken 라이브러리를 사용해 토큰을 생성하는 방법은 아래와 같다.

        // 옵션 같은 경우에는 다양하게 넣어줄 수 있는데 나는 아래와 같이 추가하였다.
        var aT_options = {
            expiresIn: "1d",
        };
        var rT_options = {
            expiresIn: "3d",
            issuer: "ejyoon",
            subject: "userinfo",
        };

        const accessToken = jwt.sign(
            payload,
            process.env.ACCESS_SECRET,
            aT_options
        );
        const refreshToken = jwt.sign(
            payload,
            process.env.REFRESH_SECRET,
            rT_options
        );

        console.log("성공");

        // res.cookies() 메소드를 통해 cookie 값 지정.
        res.cookie("refreshToken", refreshToken);
        res.status(200).send({
            data: { accessToken: accessToken },
            message: "ok",
        });
    }
};

 

 

💡 server.token/controllers/users/accessTokenRequest.js

const { Users } = require("../../models");
//json web token 라이브러리를 사용
const jwt = require("jsonwebtoken");

module.exports = (req, res) => {
    // TODO: urclass의 가이드를 참고하여 GET /accesstokenrequest 구현에 필요한 로직을 작성하세요.
    console.log(req.headers);
    // authorization 헤더 확인 후 없으면 에러 전달
    if (!req.headers.authorization) {
        res.status(400).send({ data: null, message: "invalid access token" });
    }

    // 있으면 아래와 같이 수행
    else {
        const authorization = req.headers["authorization"];
        //payload 암호화되어 있는 데이터 가져옴
        const token = authorization.split(" ")[1];
        // jwt.sign으로 암호화했다면, jwt.verify로 해독.
        const data = jwt.verify(token, process.env.ACCESS_SECRET);
        // console.log(data)
        //데이터가 없으면 에러 전달
        if (!data) {
            res.status(403).send({
                data: null,
                message: "invalid access token",
            });
        }
        // 데이터가 있다면 payload 데이터를 획득했으니, 각각의 값에 맞는 정보 찾아서 넣어줌.
        else {
            res.status(200).send({
                data: {
                    userInfo: {
                        id: data.id,
                        userId: data.userId,
                        email: data.email,
                        createdAt: data.createdAt,
                        updatedAt: data.updatedAt,
                    },
                },
                message: "o",
            });
        }
    }
};

 

💡 server.token/controllers/users/refreshTokenRequest.js

: accessTokenRequest와 거의 비슷하지만 리프레쉬 토큰으로 access 토큰을 재발급 받을 수 있음.

const { Users } = require("../../models");
//json web token 라이브러리를 사용
const jwt = require("jsonwebtoken");

module.exports = (req, res) => {
    // TODO: urclass의 가이드를 참고하여 GET /refreshtokenrequest 구현에 필요한 로직을 작성하세요.
    // 쿠키에서 refresh 토큰 가져옴.
    const isRefreshToken = req.cookies.refreshToken;
    // 리프레쉬 토큰 값이 있는지 확인, 없다면 에러 메시지.
    if (!isRefreshToken) {
        res.status(400).send({
            data: null,
            message: "refresh token not provided",
        });
        // 토큰이 유효한지 확인 후, 유효하지 않다면 에러 메시지 보냄.
    } else if (isRefreshToken === "invalidtoken") {
        res.status(400).send({
            data: null,
            message: "invalid refresh token, please log in again",
        });
        // 토큰이 유효한데 해독한 데이터가 db의 정보와 같은지 확인후, 아니라면 메시지 보냄.
    } else {
        const data = jwt.verify(isRefreshToken, process.env.REFRESH_SECRET);
        if (!data) {
            res.status(400).send({
                data: null,
                message: "refresh token has been tempered",
            });
            // 디비에 저장되어 있는 리프레쉬 토큰 정보와 쿠키에서 가져온 리프레쉬 토큰의 정보가 같다면 accessToken을 새로 생성해서 보내줘야 한다
        } else {
            // 아래와 같이 정보 가져옴
            const payload = {
                id: data.id,
                userId: data.userId,
                email: data.email,
                createdAt: data.createdAt,
                updatedAt: data.updatedAt,
            };

            // 다시 access 토큰 만들어서 전달
            const accessToken = jwt.sign(payload, process.env.ACCESS_SECRET, {
                expiresIn: "1d",
            });

            res.status(200).send({
                data: {
                    accessToken: accessToken,
                    userInfo: payload,
                },
                message: "ok",
            });
        }
    }
};
728x90

댓글