Notice
Recent Posts
Recent Comments
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
관리 메뉴

0strich

[Authentication] JWT Token 인증 본문

카테고리 없음

[Authentication] JWT Token 인증

0strich 2020. 6. 10. 15:55

프로젝트 진행 중에 세션 인증 사용할 때의 세션 저장 리소스를 줄이기 위해 JWT Token 인증 방식을 적용하기로 했다.

이번 글에서는 토큰 인증의 서버 코드를 구현할 것이다. 유저 정보를 저장하는 데이터베이스는 편의상 배열에 저장하도록 하자.

우선 jwt token 인증이 흐름이 아래 그림을 통해 확인해 보자

서버 측에서 작성해야 할 코드는 사용자가 로그인을 시도하면 사용자를 검증한 후, access, refresh token을 모두 발급해 준다.

또한 access token의 만료를 확인해주고, refresh token을 받아서 새로운 access token을 발급해줘야 한다.

Install

우선 jwt를 실습하기 위해 필요한 모듈을 설치가 필요하다. 다음 명령어들을 차례대로 실행해 보자

npm init -y
npm install --save express dotenv crypto nodemon jsonwebtoken

 

express : 서버 구축

dotenv : token 생성 시에 사용할 key 들을 저장할 환경변수 설정

crypto : token secret key를 생성

nodemon : 서버를 껐다 켜지 않고 변경사항을 적용

jsonwebtoken : jwt token 인증에 필요한 library

 

Postman을 사용해서 응답을 확인할 수도 있지만, 이번엔 좀 더 편하게 test를 하기 위해 VSC Plugin 인 Rest Client를 사용했다

secret_key 생성

cyrpto, dotevn library를 사용해서 access, refresh token 생성 시에 필요한 비밀 키들을 만들어 보자

비밀키는 해당 프로젝트 최상위 경로에 .env 파일에 저장함으로써 prcess.env로 접근할 수 있게 된다

 

crypto library를 사용하여 random key 2개를 생성해서 저장하자

.env 파일을 생성하고 소스에서 require("dotenv"). config()를 적용하면 위와 같이 process.env로 키를 사용할 수 있게 된다.

키 생성을 했으니 해당 키를 사용해서 app.js 코드를 작성해 보자.

app.js

const express = require("express");
const jwt = require("jsonwebtoken");
const app = express();
const port = 4001;
require("dotenv").config();

app.use(express.json());

// Users Information
const users = [
  {
    id: 1,
    username: "A",
    password: "1234",
    email: "A@gmail.com",
  },
  {
    id: 2,
    username: "B",
    password: "qwer",
    email: "B@gmail.com",
  },
];

// accessToken 생성기
const generateAccessToken = (payload) => {
  return jwt.sign(payload, process.env.ACCESSTOKEN_SECRET_KEY, {
    expiresIn: "360s",
  });
};

// 로그인
app.post("/login", (req, res) => {
  try {
    const { username, password } = req.body;
    // 검증 예시
    const exist = users.filter(
      (obj) => obj.username === username && obj.password === password
    );

    if (exist[0]) {
      const payload = { username, password };
      const accessToken = generateAccessToken(payload);
      const refreshToken = jwt.sign(
        payload,
        process.env.REFRESHTOKEN_SECRET_KEY,
        {
          expiresIn: "36000s",
        }
      );
      res.status(200).send({ accessToken, refreshToken });
    }
  } catch (err) {
    console.log(err);
  }
});

// accessToken 유효성 검사
app.get("/check", (req, res) => {
  const authHeader = req.headers["authorization"];
  const token = authHeader.split(" ")[1];

  if (!token) {
    res.status(401).send({ tokenMissing: "not exist token" });
  } else {
    jwt.verify(token, process.env.ACCESSTOKEN_SECRET_KEY, (err, payload) => {
      if (err) {
        res.status(403).send({ error: err });
      } else {
        res.status(200).send(payload);
      }
    });
  }
});

// refresh token 으로 새로운 access token 요청
app.post("/token", (req, res) => {
  const refreshToken = req.body.token;
  jwt.verify(
    refreshToken,
    process.env.REFRESHTOKEN_SECRET_KEY,
    (err, payload) => {
      if (err) {
        console.log("err ==> ", err);
        res.sendStatus(403);
      } else {
        const { username, password } = payload;
        const accessToken = generateAccessToken({ username, password });
        res.status(200).send({ accessToken });
      }
    }
  );
});

app.listen(port, () => {
  console.log(`listen on port ${port}`);
});

요약하자면 다음과 같다

1. 임시 유저 정보를 저장

2. access token을 생성해주는 함수

3. login API : 유저 정보 확인 후 access, refresh token을 생성해서 응답

4. check API : access token을 레더로 받아서 유효한지 확인

5. token API : refresh token을 받아서 새로운 access token 발급 

Rest Client 확인

login API : 올바른 유저 정보로 요청을 보낼 경우 access, refresh token 응답을 받을 수 있다.

check API : 유효한 access token 인 경우 token을 생성할 때 사용된 payload를 응답받을 수 있다.

token API : 유효한 refresh token으로 요청할 경우 새로운 access token을 발급받을 수 있다.

Comments