본문 바로가기
Nodejs

[Node.js]JWT 사용자 인증

by teamnova 2023. 10. 5.

안녕하세요 이번 시간에는 JWT, JSON Web Token 에 대해서 알아보겠습니다.

 

JWT 란?

JWT는 JSON 객체를 사용하여 정보를 안전하게 전달하기 위한 코딩된 토큰입니다. 주로 웹에서 사용자 인증에 사용되며, 서버와 클라이언트 간에 정보를 안전하게 전송하기 위한 수단으로 활용됩니다.
Express.js와 ejs 를 사용하여 JWT를 생성, 검증 및 디코드하는 간단한 프로젝트를 만드는 방법을 알아보겠습니다.

먼저 구현을 위해 필요한 패키지 설치를 합니다

npm install express jsonwebtoken body-parser ejs

프로젝트 구조는 다음과 같습니다

/jwt-express-test
|-- /views
|   |-- index.ejs
|-- app.js

 

app.js

const express = require('express');
const jwt = require('jsonwebtoken');
const bodyParser = require('body-parser');
const app = express();
const path = require('path');
const secretKey = 'your_secret_key';
const algorithm = 'HS256';

// 바디 파서를 사용하여 JSON 요청을 처리합니다.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

// EJS 템플릿 엔진을 사용하도록 설정합니다.
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));

// 토큰에서 "Bearer " 문자열을 제거하여 순수한 토큰 문자열을 추출합니다.
const extractToken = (headerValue) => {
    if (headerValue && headerValue.startsWith('Bearer ')) {
        return headerValue.slice(7);
    }
    return null;
};

// JWT의 내용을 디코딩하여 확인합니다.
app.get('/decode', (req, res) => {
    const token = extractToken(req.headers.authorization);
    if (!token) {
        return res.status(401).json({ message: 'Token not provided.' });
    }
    const decodedPayload = jwt.decode(token);
    if (!decodedPayload) {
        return res.status(401).json({ message: 'Invalid token.' });
    }
    res.json(decodedPayload);
});

// JWT의 유효성을 검증합니다.
app.get('/verify', (req, res) => {
    const token = extractToken(req.headers.authorization);
    if (!token) {
        return res.status(401).json({ message: 'Token not provided.' });
    }
    jwt.verify(token, secretKey, { algorithms: [algorithm] }, (err, decoded) => {
        if (err) {
            return res.status(401).json({ message: 'Invalid token.' });
        }
        res.json({ message: 'Token is valid.', user: decoded.user });
    });
});

// 사용자에게 JWT를 발급합니다.
app.get('/token', (req, res) => {
    const payload = {
        user: 'user_id',
        role: 'admin'
    };
    const token = jwt.sign(payload, secretKey, { expiresIn: '1h', algorithm });
    res.json({ token });
});

// 루트 경로에 EJS 템플릿을 렌더링합니다.
app.get('/', (req, res) => {
    res.render('index');
});

app.listen(3000, () => {
    console.log('Server started on http://localhost:3000');
});

 

EJS 코드

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JWT Test</title>
  </head>

  <body>
    <h2>JWT Test Page</h2>

    <button id="getToken">Get Token</button>
    <p><strong>Token:</strong> <span id="token"></span></p>

    <h3>Verify Token:</h3>
    <textarea id="inputToken" rows="3" style="width: 100%"></textarea>
    <!-- 토큰 입력 부분 -->
    <button id="verifyToken">Verify Token</button>
    <p>
      <strong>Verification Result:</strong>
      <span id="verificationResult"></span>
    </p>

    <button id="decodeToken">Decode Token</button>
    <p><strong>Decoded Info:</strong> <span id="decoded"></span></p>

    <script>
      // 타임스탬프를 'YYYY-MM-DD HH:mm:ss' 형식의 날짜 문자열로 변환합니다.
      function convertToDateTime(timestamp) {
        const date = new Date(timestamp * 1000);
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, "0");
        const day = String(date.getDate()).padStart(2, "0");
        const hours = String(date.getHours()).padStart(2, "0");
        const minutes = String(date.getMinutes()).padStart(2, "0");
        const seconds = String(date.getSeconds()).padStart(2, "0");
        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
      }

      // 토큰을 받아옵니다.
      document
        .getElementById("getToken")
        .addEventListener("click", async () => {
          const response = await fetch("/token");
          const data = await response.json();
          document.getElementById("token").textContent = data.token;
        });

      // 입력된 토큰의 유효성을 검증합니다.
      document
        .getElementById("verifyToken")
        .addEventListener("click", async () => {
          const token = document.getElementById("inputToken").value;
          const response = await fetch("/verify", {
            headers: {
              Authorization: "Bearer " + token,
            },
          });
          const data = await response.json();
          if (data.message && data.message === "Invalid token.") {
            alert("올바르지 않은 토큰입니다."); // 팝업으로 메시지 표시
          }
          document.getElementById("verificationResult").textContent =
            data.message || JSON.stringify(data, null, 2);
        });

      // 받아온 토큰의 내용을 디코드합니다.
      document
        .getElementById("decodeToken")
        .addEventListener("click", async () => {
          const token = document.getElementById("token").textContent;
          const response = await fetch("/decode", {
            headers: {
              Authorization: "Bearer " + token,
            },
          });
          const data = await response.json();
          if (data.iat) {
          //발행시간
            data.iat = convertToDateTime(data.iat);
          }
          if (data.exp) {
          //만료시간
            data.exp = convertToDateTime(data.exp);
          }
          document.getElementById("decoded").textContent = JSON.stringify(
            data,
            null,
            2
          );
        });
    </script>
  </body>
</html>

 

시연 영상입니다

 

 

[참고자료]

https://en.wikipedia.org/wiki/JSON_Web_Token

 

JSON Web Token - Wikipedia

From Wikipedia, the free encyclopedia JSON-based standard for passing claims between parties in web application environments JSON Web Token (JWT, pronounced , same as the word "jot"[1]) is a proposed Internet standard for creating data with optional signat

en.wikipedia.org

https://jwt.io/introduction

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io