debian:trixie 컨테이너 환경에서 작성된 글입니다.

공부에 gpt를 사용한 글입니다.


JWT (JSON Web Token)

사용자의 인증 정보를 위조되지 않도록 서명해서 전달하는 토큰 형식.

데이터는 JSON 형태로 담겨 있으며 Base64로 인코딩되어 전송된다.

주로 로그인 인증, API 인증, 서버 간 인증에 사용된다.

쉽게 말하면 '사용자가 누구인지 증명하는 서명된 신분증' 이다.

 

단 서버입장에서는 토큰을 가진 사람 = 그 사용자 라고 간주하기 때문에 토큰의 전달과정은 암호화 되어있어야 한다.

 

JWT는 세가지 부분으로 구성된다.각 부분은 아래와 같이 . 으로 구분된다.

  1. Header 헤더
    토큰의 타입(JWT)과 사용중인 암호화 알고리즘이 담김.
    ex) { "alg": "HS256", "typ": "JWT" }
  2. Payload 페이로드
    실제 담고싶은 데이터인 Claim이 들어간다. ex user_id, nickname, exp(만료시간)
    누구나 Base64로 디코딩해서 볼 수 있으므로 비밀번호같은 민감한 정보는 넣지 않는다.
    ex) { "userId": 1, "role": "admin" }
  3. Signature 서명
    header + payload를 비밀키로 서명한 값.
    signature = HMAC_SHA256(base64(header) + "." + base64(payload), secret_key) 이런식으로 서명을 만든다.

최종적으로 아래와 같은 header.payload.signature 의 형태가 된다.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJ1c2VySWQiOjEsIm5hbWUiOiJKb2huIn0
.
abc123signature

JWT가 위변조를 막을 수 있는 이유

누군가 Payload를 role: user → role: admin로 바꾼다고 하자.

서버는 토큰에 적힌 헤더+페이로드로 해싱을 해서 서명을 다시만들고, jwt에 적힌 서명과 일치하는지 확인한다.

하지만 둘은 일치할 수가 없다. 같은 비밀키로, 다른 내용을 해싱했기 때문. 그래서 jwt가 위조됐음을 알 수 있다.

jwt의 장/단점

장점

  • stateless 무상태 : 서버는 아무것도 저장하지 않고도 사용자를 인증할 수 있다.
  • self-contained 자체포함 : 페이로드에 사용자 정보가 담겨있기 때문에 db조회가 필요없다.

단점

  • 토큰을 강제로 만료시킬 수 없다. 상태를 저장하지 않기 때문에 토큰이 탈취되면 만료될 때까지 막을 수 없다.
  • 페이로드는 암호화 되지 않기때문에 누구나 내용을 볼 수 있다.
  • ID와 같은 정보는 몇바이트 안되는데, JWT는 수백바이트에 달한다. 매 요청마다 보내야 한다면 무시못할 오버헤드가 된다.

jwt-cpp 사용하기

1. 깃허브 클론하기

jwt-cpp는 헤더파일만으로 사용할 수 있는 헤더 전용 라이브러리다. 

 

GitHub - Thalhammer/jwt-cpp: A header only library for creating and validating json web tokens in c++

A header only library for creating and validating json web tokens in c++ - Thalhammer/jwt-cpp

github.com

git clone https://github.com/Thalhammer/jwt-cpp.git

jwt-cpp 폴더가 clone 한 위치에 생긴다. 

cp -r jwt-cpp/include .
rm -r jwt-cpp

jwt-cpp폴더 안의 include안에 있는 헤더파일들만 쓰면 된다.

2. 헤더 include 경로설정

컴파일 단계에서 -I폴더명 을 써주자. 지금은 폴더이름이 include니까 아래와같이 써준다.

참고로 현재 디렉토리 안의 파일들은 이렇다.

-Iinclude

3. 의존 라이브러리 설치

apt install -y libssl-dev

libcrypto (openssl or compatible)

libssl-dev (for the header files)

헤더파일들에서 위 두 라이브러리를 사용한다고 한다. (libcrypto는 libssl-dev를 설치할때 같이 설치된다.)

4. 소스코드

getenv("JWT_SECRET_KEY") -> 비밀키는 env에서 가져옴. 비밀키니까 하드코딩해서 넣지는 말자.

#include <string>
#include <iostream>
#include <jwt-cpp/jwt.h>

using namespace std;

//토큰 발급자가 사용하는 함수
string CreateAccessToken(const string& user_id, const string& nickname)
{
    // 비밀키는 환경변수에서 가져옴
    const string SECRET_KEY = getenv("JWT_SECRET_KEY");

    auto token = jwt::create()
        .set_issuer("auth_server")          // 발급자
        .set_type("JWT")
        .set_payload_claim("user_id",  jwt::claim(user_id))
        .set_payload_claim("nickname", jwt::claim(nickname))
        .set_issued_at(chrono::system_clock::now())
        .set_expires_at(chrono::system_clock::now() 
                        + chrono::hours(1))    // 1시간
        .sign(jwt::algorithm::hs256{SECRET_KEY});   // 비밀키로 서명

    return token;
}

//비밀키를 공유하는 다른 서버(혹은 발급자)가 토큰의 유효성을 검사 할 때 사용하는 함수
bool VerifyAccessToken(const string& token, string& out_user_id, string& out_nickname)
{
    // 비밀키는 환경변수에서 가져옴
    const string SECRET_KEY = getenv("JWT_SECRET_KEY");

    try
    {
        auto verifier = jwt::verify()
            .allow_algorithm(jwt::algorithm::hs256{SECRET_KEY})
            .with_issuer("auth_server");    // 발급자 확인
        
        auto decoded = jwt::decode(token);
        verifier.verify(decoded);           // 서명 + 만료시간 자동 검증
        
        out_user_id = decoded.get_payload_claim("user_id").as_string();
        out_nickname = decoded.get_payload_claim("nickname").as_string();
        return true;
    }
    catch (const exception& e)
    {
		cout << "exeption" << endl;
        // 서명 불일치, 만료, 형식 오류 전부 여기로 떨어짐
        return false;
    }
}

int	main()
{
	string	token = CreateAccessToken("123", "dodontak");
	string	tokenId;
	string	tokenNickname;
	if (VerifyAccessToken(token, tokenId, tokenNickname))
	{
		cout << "success!" << endl;
		cout << tokenId << ": " << tokenNickname << endl;
	}
	else
	{
		cout << "fail!" << endl;
	}
}

 

CreateAccessToken 함수의 jwt::create 부분을 해설하면 이렇다.

jwt::create()
    .set_issuer("auth_server")    // 페이로드
    .set_type("JWT")              // 헤더
    .set_payload_claim("user_id",  jwt::claim(user_id))   // 페이로드
    .set_payload_claim("nickname", jwt::claim(nickname))  // 페이로드
    .set_issued_at(...)           // 페이로드
    .set_expires_at(...)          // 페이로드
    .sign(...)                    // 서명 생성 (헤더+페이로드 확정 후 Signature 계산)

 

 

'공부 > C\C++' 카테고리의 다른 글

정규 표현식 with.C++  (0) 2026.03.17
c++ friend  (0) 2025.07.24
c++ 중괄호 초기화 Brace initialization  (0) 2025.07.13
c union  (0) 2025.07.05
c++ placement new  (0) 2025.07.04

+ Recent posts