FastAPI에서 JWT 토큰을 이용한 로그인 인증 구현

2025. 5. 21. 14:15·Python

FastAPI에서 JWT 토큰을 이용한 로그인 인증 구현

1. JWT란?

JWT(Json Web Token)는 서버가 사용자 인증을 위해 발급하는 토큰으로, 사용자 정보를 암호화하여 클라이언트에 전달하는 방식입니다.

클라이언트는 이 토큰을 Authorization: Bearer <토큰> 형태로 서버에 전달하고, 서버는 토큰을 검증하여 인증 여부를 판단합니다.

JWT는 다음의 3부분으로 구성됩니다.

  • Header: 알고리즘, 토큰 타입
  • Payload: 사용자 정보 및 만료 시간 등 클레임
  • Signature: 헤더 + 페이로드를 비밀키로 암호화한 서명

Claim 종류

  • iat: 발급 시간
  • exp: 만료 시간
  • sub: 사용자 ID 등 식별 정보

  • 헤더: 토큰 타입(typ)과 암호화 방법(alg)을 보관하는 곳으로 BASE64로 인코딩
  • 페이로드: 다양한 종류의 정보를 담는 곳으로 BASE64로 인코딩

2. 관련 패키지 설치

pip install python-jose[cryptography] passlib[bcrypt] python-multipart
  • python-jose: JWT 인코딩 및 디코딩
  • passlib: 비밀번호 해싱
  • python-multipart: OAuth2 토큰 전달 시 multipart 처리 지원

3. 비밀번호 해싱 유틸리티

# auth/hash_password.py
from passlib.context import CryptContext

class HashPassword:
    def __init__(self):
        self.pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

    def hash_password(self, password: str):
        return self.pwd_context.hash(password)

    def verify_password(self, plain_password: str, hashed_password: str):
        return self.pwd_context.verify(plain_password, hashed_password)

4. 회원가입 / 로그인

# routes/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlmodel import select
from auth.hash_password import HashPassword
from database.connection import get_session
from models.users import User, UserSignIn, UserSignUp

user_router = APIRouter(tags=["User"])
hash_password = HashPassword()

@user_router.post("/signup", status_code=201)
async def sign_new_user(data: UserSignUp, session = Depends(get_session)):
    user = session.exec(select(User).where(User.email == data.email)).first()
    if user:
        raise HTTPException(status_code=409, detail="동일한 사용자가 존재합니다.")
    
    new_user = User(
        email=data.email,
        password=hash_password.hash_password(data.password),
        username=data.username
    )
    session.add(new_user)
    session.commit()
    session.refresh(new_user)
    return {"message": "사용자 등록이 완료되었습니다.", "user": new_user}

5. .env 환경 변수 설정

# .env
DATABASE_URL=mysql+pymysql://fastapiuser:p%40ssw0rd@localhost:3306/fastapidb
SECRET_KEY=SECRET+FASTAPI:

6. 환경 설정 클래스

# database/connection.py
from pydantic_settings import BaseSettings
from typing import Optional
from sqlmodel import SQLModel, create_engine, Session

class Settings(BaseSettings):
    DATABASE_URL: Optional[str]
    SECRET_KEY: Optional[str]

    class Config:
        env_file = ".env"

settings = Settings()
engine_url = create_engine(settings.DATABASE_URL, echo=True)

def conn():
    SQLModel.metadata.create_all(engine_url)

def get_session():
    with Session(engine_url) as session:
        yield session

7. JWT 생성 및 검증

# auth/jwt_handler.py
from time import time
from fastapi import HTTPException, status
from jose import jwt
from database.connection import settings

def create_jwt_token(email: str, user_id: int) -> str:
    payload = {
        "user": email,
        "user_id": user_id,
        "iat": time(),
        "exp": time() + 3600
    }
    return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")

def verify_jwt_token(token: str):
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=["HS256"])
        if payload.get("exp") is None or time() > payload["exp"]:
            raise HTTPException(status_code=403, detail="Token expired")
        return payload
    except:
        raise HTTPException(status_code=400, detail="Invalid token")

8. 로그인 시 JWT 토큰 발급

# routes/users.py
from auth.jwt_handler import create_jwt_token

@user_router.post("/signin")
async def sign_user_in(data: UserSignIn, session=Depends(get_session)):
    user = session.exec(select(User).where(User.email == data.email)).first()
    if not user or not hash_password.verify_password(data.password, user.password):
        raise HTTPException(status_code=401, detail="이메일 또는 비밀번호가 일치하지 않습니다.")
    
    access_token = create_jwt_token(user.email, user.id)
    return {
        "message": "로그인 성공",
        "user": user.username,
        "access_token": access_token
    }

9. 인증된 사용자만 접근 가능한 API 구성

# routes/events.py
from auth.authenticate import authenticate

@event_router.post("/", status_code=201)
async def create_event(
    data: Event = Body(...),
    session = Depends(get_session),
    user_id: int = Depends(authenticate)  # 인증 사용자
):
    event = Event(**data.dict(), user_id=user_id)
    session.add(event)
    session.commit()
    session.refresh(event)
    return {"message": "이벤트 등록 완료", "user_id": user_id}

10. Swagger UI에서 토큰 인증 테스트

  1. /users/signin에서 로그인 → access_token 복사
  2. 상단 Authorize 버튼 클릭
  3. Bearer <access_token> 형식으로 붙여넣기
  4. 인증이 필요한 API 호출 시 자동으로 헤더 포함

11. 전체 인증 흐름 요약

단계 설명
1. 회원가입 비밀번호 해싱 후 DB에 저장
2. 로그인 해시 검증 후 JWT 토큰 발급
3. 토큰 전달 Authorization: Bearer 토큰 헤더로 요청
4. 인증 라우터 보호 Depends(authenticate)로 인증 강제
5. Swagger 테스트 Authorize 버튼으로 토큰 입력 후 실행

※ JWT 토큰 내부 구조 예시

 

'Python' 카테고리의 다른 글

FastAPI + React로 회원관리 시스템 구축 (MySQL 연동)  (0) 2025.05.21
React와 Python 연결  (0) 2025.05.20
FastAPI-DB연결  (0) 2025.05.18
FastAPI-CRUD  (0) 2025.05.16
Path, Query, Response_model  (0) 2025.05.15
FastAPI 백엔드 개발 환경 세팅 & 기본 API 구현 정리  (0) 2025.05.14
'Python' 카테고리의 다른 글
  • FastAPI + React로 회원관리 시스템 구축 (MySQL 연동)
  • React와 Python 연결
  • FastAPI-DB연결
  • FastAPI-CRUD
jaeon.cloud
jaeon.cloud
  • jaeon.cloud
    JEONG JAE ON
    jaeon.cloud
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • Docker (8)
      • Cloud (7)
      • AWS (14)
      • Network (9)
      • Linux (1)
      • Python (7)
      • Javascript (3)
      • React (5)
      • Project (3)
      • Kubernetes (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    AWS
    네트워크
    http
    react
    Python
    3-tier Architecture
    docker
    클라우드
    cors
    lambda
    kubernetes
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
jaeon.cloud
FastAPI에서 JWT 토큰을 이용한 로그인 인증 구현
상단으로

티스토리툴바