FastAPI-JWT

김기훈·2025년 10월 24일

FastAPI

목록 보기
6/7

JWT(Json Web Token)

라이브러리

  • 주로 사용자 인증(Authentication) 과 인가(Authorization) 를 처리할 때 사용
    • ex. 로그인 시 클라이언트에게 토큰을 발급하고, 이후 요청마다 이 토큰을 헤더에 담아 인증을 수행
개념설명
JWTJSON 형태로 인코딩된 토큰. 서버는 로그인 시 유저 정보를 암호화해 토큰을 발급함.
Header어떤 알고리즘으로 암호화했는지를 나타냄 (예: HS256).
Payload사용자 정보(예: user_id, username)와 만료 시간(exp) 등을 담음.
Signature서버 비밀키로 Header + Payload 를 서명해 위조 방지용으로 사용.


예시

from datetime import datetime, timedelta
# datetime: 현재 시각을 얻기 위해 사용(토큰 만료시간 계산용)
# timedelta: 특정 시간 차이(예: 30분 후)를 계산할 때 사용
from typing import Optional
# 타입 힌팅에서 Optional은 “값이 있거나(None일 수도 있는)” 타입을 의미
from fastapi import FastAPI, Depends, HTTPException, status
# status: 표준화된 상태 코드 모듈 (예: status.HTTP_401_UNAUTHORIZED)
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
# OAuth2PasswordBearer: “Bearer 토큰”을 통해 인증하는 데 사용되는 의존성
# OAuth2PasswordRequestForm: 로그인 시 username, password를 form-data로 받을 때 사용
from jose import JWTError, jwt
# jwt: JWT 토큰을 인코딩/디코딩하는 모듈
# JWTError: JWT 검증 과정에서 오류 발생 시 잡기 위한 예외 클래스
from pydantic import BaseModel
# JWT 설정(실제 서비스에서는 .env로 숨겨야 함)
SECRET_KEY = "secret-key-example" 
ALGORITHM = "HS256" # JWT 암호화 방식 (대부분 HMAC-SHA256 사용)
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # 토큰 만료 시간 (30분 후 만료)

# 가짜 유저 DB
fake_user_db = {
    "kihoon": {
        "username": "kihoon",
        "password": "1234",
    }
}

# JWT 토큰 생성을 위한 함수
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy() # 토큰 안에 담을 데이터를 복사 
    expire = datetime.now() + (expires_delta or timedelta(minutes=15))
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    # SECRET_KEY와 ALGORITHM을 이용해 JWT 인코딩 (암호화)
    return encoded_jwt

# JWT 검증용 의존성
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/login")
# 클라이언트가 Authorization: Bearer <token> 형태로 보낸 토큰을 자동으로 추출
# tokenUrl="/login": 토큰을 발급받을 경로(/login)를 지정

app = FastAPI()

class Token(BaseModel): # 로그인 성공 시 반환할 데이터 구조 정의
    access_token: str
    token_type: str

class User(BaseModel): # /users/me 요청 시 반환할 유저 정보 구조 정의
    username: str


@app.post("/login", response_model=Token)
def login(form_data: OAuth2PasswordRequestForm = Depends()):
# Depends()를 통해 form_data 자동 주입 (username, password를 받음)
    user = fake_user_db.get(form_data.username)
    if not user or user["password"] != form_data.password:
        raise HTTPException(status_code=400, detail="Invalid credentials")

    access_token = create_access_token(data={"sub": user["username"]})
    return {"access_token": access_token, "token_type": "bearer"}


@app.get("/users/me", response_model=User)
def read_users_me(token: str = Depends(oauth2_scheme)):
# Depends(oauth2_scheme)로 Authorization 헤더의 토큰을 자동 추출
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        # 전달받은 토큰을 SECRET_KEY로 복호화
        username = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="Invalid token")
        return {"username": username}
    except JWTError:
        raise HTTPException(status_code=401, detail="Invalid token")
profile
안녕하세요.

0개의 댓글