nodejs로 jwt 로그인 구현

JWT란

JSON Web Token의 줄임말이다. 해싱된 문자열로 구성되어 있는데, 3등분해서 볼 수 있다. jwt_string
위에서 . 단위로 끊어서 3등분한다. 가장 첫 부분부터 HEADER,PAYLOAD,SIGNATUER로 볼 수 있다.
HEADER는 토큰의 타입, 암호화 알고리즘의 정보를 가지고 있다,
PAYLOAD는 정보가 저장되어 있는데, key value로 구성 되어있다.
PAYLOAD는 secret key가 암호화되어 있다.


왜 필요하게 되었나?

stateful,stateless에서 말했던 것처럼, 서버의 scale out 등 문제의 방지와 많은 세션을 서버에서 부담하는 문제를 줄이기 위한 방안으로 좋다.


Node에서의 JWT

JWT를 알게된지 얼마 안되어, 개념이 잘 안잡혀 직접 구현해보기로 했다. node에서 JWT를 사용하려면 우선 모듈을 설치해야 한다.

npm i jsonwebtoken --save

그 후, 간단한 서버를 띄우고, 로그인 url을 설정해 준다.

import express from "express"
import cookieParser from "cookie-parser"
import fetch from "node-fetch"
import { getUserId, login } from "./api/authController";

const app = express();

...중략

app.route("/login")
    .get((req, res) => {
        return res.render("login")
    })
    .post(login)

그 후 import한 login controller를 작성한다.

import jwt from "jsonwebtoken";

export const login = (req, res) => {
  const { id, password } = req.body;
  const user = { id };

  //무조건 로그인이 된다는 조건.
  const accessToken = jwt.sign(user, process.env.JWT_SECRET);

  res.cookie("accessToken", accessToken, {
    maxAge: 300000,
    signed: true,
    httpOnly: true,
  });

  return res.redirect("/");
};

JWT를 시연만 목적으로만 했기에, 로그인 유효성 검사를 생략한 채로, 무조건 로그인 가능하게 했다. 그리고 해당 JWT 토큰은 따로 Client단에서 로그인 요청을 할 시, 유효성 검사 후, json형태로 토큰을 발급해 해당 서버에서 cookie로 저장해야 한다. 위 구현과는 다소 차이가 있다. 코드 구성은 jwt.sign에 Client가 입력한 로그인 정보를 삽입한 뒤, secret을 주입해 토큰을 발급받게 하였다. 그 후, 토큰을 cookie에 저장한다. cookie 옵션으로 httpOnly로 브라우저단에서 javascript를 통해 접근하는 걸 막게 하였다.

app.get("/posts", async(req, res) => {

    //임시데이터
    const posts = [
        {
            id: 유저ID,
            title:  제목
        },  ...
    ]

    const header = {
        method:'GET',
        headers: {
            Authorization:`Bearer ${req.signedCookies.accessToken}`
        }
    }
    const fullURL = req.protocol + '://' + req.get('host');
    const user = await (await fetch(`${fullURL}/getUser`, header)).json();

    return res.render("posts", {posts:posts.filter(item => item.id === user.id)})
})

로그인한 유저 본인이 올린 글만 볼 수 있는 페이지를 방문할 때, 헤더에 Authorization를 설정하고 Bearer을 인증타입 설정 후, 토큰을 붙혀 서버에게 해당 유저의 ID정보를 요청한다. 그 후, 해당 유저와 작성자가 동일한 글 목록들을 보여줌으로 구현했다.

export const getUserId = async (req, res) => {
  const authHeader = req.headers["authorization"];
  const token = authHeader.split(" ")[1];

  await jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.end();
    return res.json(user);
  });
};

Client Side에서 토큰으로 유저의 ID정보를 요청하면 jwt.verify로 인증 후, 정상적이라면 json형식으로 반환해주도록 구현했다.

정리

처음 stateful, stateless같은 방식을 개념적으로만 알았을 때, 어떻게 만들지? 라는 애매한 느낌이 있었다. 하지만 이번 예시를 해보며, 많은 걸 알게되었는데, api가 구성되는 방식, 타 서버에서는 어떻게 로그인을 관리하는지, 어디에 저장할 지… 많은걸 찾아보고 어렴풋이 알게되어 좋은 시간이었다.
위에 구현한 JWT인증방식은 많은 문제점이 있는데, 우선, accessToken의 기한이 없다는 것이다. 이에 refreshToken이 있는데, 다음 포스팅에 해보려고 한다.

댓글남기기