JWT(Json Web Token)란 무엇인가?
JWT는 사용자 인증을 위해 서버와 클라이언트가 서명된 토큰(JSON 형식)을 주고받는 방식입니다.
토큰 기반 인증의 대표적인 기술로, REST API 구조에서 세션 없이 인증 상태를 유지할 수 있습니다.
JWT의 구성
header.payload.signature
- Header: 토큰의 유형(JWT)과 해시 알고리즘
- Payload: 사용자 ID, 권한 등 클레임(정보)
- Signature: 비밀키로 서명한 해시값 (위조 방지)
FastAPI에서 JWT를 쓰는 이유
- 세션 없이 API 간 인증 처리 가능
- 모바일/프론트엔드와 연동 용이
- OAuth2 기반으로 확장성 확보
FastAPI에서 JWT 인증 구현하기
FastAPI에서는 python-jose
패키지를 이용해 JWT 토큰을 발급하고 검증할 수 있습니다.
이 예제에서는 로그인 → 토큰 발급 → 보호된 엔드포인트 접근 순으로 구현합니다.
1. 패키지 설치
pip install fastapi[all] python-jose[cryptography] passlib[bcrypt]
2. 사용자 인증 + 토큰 발급 예제
main.py
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jose import JWTError, jwt
from datetime import datetime, timedelta
from typing import Optional
from passlib.context import CryptContext
app = FastAPI()
# 환경 설정
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# 비밀번호 해싱
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# OAuth2 스킴 선언
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
# 가상 사용자 DB
fake_users_db = {
"admin": {
"username": "admin",
"hashed_password": pwd_context.hash("password123"),
}
}
def verify_password(plain, hashed):
return pwd_context.verify(plain, hashed)
def authenticate_user(username: str, password: str):
user = fake_users_db.get(username)
if not user or not verify_password(password, user["hashed_password"]):
return False
return user
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=15))
to_encode.update({"exp": expire})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
# 토큰 발급 엔드포인트
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(form_data.username, form_data.password)
if not user:
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("/protected")
async def protected_route(token: str = Depends(oauth2_scheme)):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise HTTPException(status_code=401, detail="Invalid token")
return {"message": f"Welcome, {username}!"}
except JWTError:
raise HTTPException(status_code=401, detail="Token verification failed")
3. 테스트 방법
- 1)
POST /token
에 username=admin, password=password123 전송 - 2) 응답에서 받은 access_token을
Authorization: Bearer <token>
헤더로GET /protected
요청
FastAPI는 Swagger UI에서도 OAuth2 기반 인증 테스트가 가능합니다./docs
→ "Authorize" → Bearer 토큰 입력 → API 호출 테스트
JWT 인증 보안 강화 및 확장
1. 토큰 만료 시간 조절
ACCESS_TOKEN_EXPIRE_MINUTES = 60 # 1시간
기본 만료 시간을 설정해두고 필요시 refresh token
을 구현해 장기 인증을 유지할 수 있습니다.
2. 비밀키는 환경변수로 분리
import os
SECRET_KEY = os.getenv("JWT_SECRET", "development_key")
실제 운영 환경에서는 SECRET_KEY
를 코드에 직접 쓰지 않고 .env나 CI 환경 변수로 관리해야 합니다.
3. 사용자 역할 기반 권한 처리
# payload에 role 추가
data={"sub": "admin", "role": "admin"}
# protected route에서 검사
if payload.get("role") != "admin":
raise HTTPException(status_code=403, detail="접근 권한 없음")
JWT 안에 role
같은 클레임을 포함시키면 사용자 권한에 따라 접근 제한이 가능합니다.
4. Refresh Token 구현
- Access Token은 짧은 만료 시간으로 설정
- Refresh Token은 DB에 저장하여 관리 (만료되면 재로그인 필요)
Refresh Token을 사용하면 장기 로그인 유지와 중간 로그아웃 처리를 유연하게 구현할 수 있습니다.FastAPI는 OAuth2 + JWT 인증을 기본 지원하며, 문서화와 토큰 발급 시스템까지 손쉽게 구축할 수 있습니다.이제 여러분도 단 몇 줄의 코드로 보안성과 확장성을 갖춘 API 인증 시스템을 만들 수 있습니다.지금부터 FastAPI에 JWT 인증을 도입해보고, 토큰 기반 인증의 유연함과 편리함을 직접 체험해보세요!