백엔드 기초집: Python FastAPILESSON 22
JWT 로그인과 인증 미들웨어/의존성
난이도초급
예상 시간35분
선수지식이전 강의
22강. JWT 로그인과 인증 미들웨어/의존성
1. 이번 강의에서 해결할 문제
서버는 각 기록 요청이 어떤 사용자에게서 왔는지 알 수 없습니다. 로그인에서 JWT를 발급하고 Bearer token으로 현재 사용자를 확인합니다.
2. 학습 목표
3. 핵심 개념
JWT는 내용을 숨기는 암호문이 아니라 서명된 token입니다. 비밀 정보를 payload에 넣지 않고 sub에는 앱 전체에서 고유한 문자열 식별자를 넣습니다. exp가 지나면 거부하며 HTTPS 없이 token을 전송해서는 안 됩니다. 인증은 여러 endpoint가 재사용하므로 middleware보다 값을 반환하는 dependency가 잘 맞습니다.
4. 단계별 코드 예시
파일 경로: game-ranking-api/app/schemas/token.py
app/schemas/token.py
from pydantic import BaseModel
class Token(BaseModel):
access_token: str
token_type: str = "bearer"
파일 경로: game-ranking-api/app/security.py에 추가
app/security.py
from datetime import datetime, timedelta, timezone
import jwt
from jwt.exceptions import InvalidTokenError
from app.config import settings
ALGORITHM = "HS256"
def create_access_token(subject: str) -> str:
expires_at = datetime.now(timezone.utc) + timedelta(minutes=settings.access_token_expire_minutes)
return jwt.encode({"sub": subject, "exp": expires_at}, settings.jwt_secret, algorithm=ALGORITHM)
파일 경로: game-ranking-api/app/dependencies.py에 추가
app/dependencies.py
from typing import Annotated
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jwt.exceptions import InvalidTokenError
from sqlalchemy.orm import Session
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
def get_current_user(
token: Annotated[str, Depends(oauth2_scheme)],
db: Annotated[Session, Depends(get_db)],
) -> User:
credentials_error = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="유효한 인증 정보가 필요합니다",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, settings.jwt_secret, algorithms=[ALGORITHM])
subject = payload.get("sub")
user_id = int(subject) if subject is not None else None
except (InvalidTokenError, ValueError):
raise credentials_error
user = db.get(User, user_id)
if user is None:
raise credentials_error
return user
파일 경로: game-ranking-api/app/routers/auth.py에 추가
app/routers/auth.py
from typing import Annotated
from fastapi import Depends
from fastapi.security import OAuth2PasswordRequestForm
@router.post("/token", response_model=Token)
def login(
form: Annotated[OAuth2PasswordRequestForm, Depends()],
db: DbSession,
) -> Token:
user = db.scalar(select(User).where(User.username == form.username.lower()))
if user is None or not verify_password(form.password, user.password_hash):
raise HTTPException(status_code=401, detail="사용자 이름 또는 비밀번호가 올바르지 않습니다")
return Token(access_token=create_access_token(str(user.id)))
실행 위치: game-ranking-api/
python -m uvicorn app.main:app --reload
$token = (Invoke-RestMethod -Method Post http://127.0.0.1:8000/auth/token -ContentType 'application/x-www-form-urlencoded' -Body 'username=player_one&password=correct-horse-battery').access_token
Invoke-RestMethod http://127.0.0.1:8000/users/me -Headers @{Authorization="Bearer $token"}
5. 코드가 동작하는 이유
로그인이 비밀번호를 verify한 뒤 user id와 만료 시간을 서명합니다. 인증 dependency가 Bearer token을 추출하고 허용 알고리즘을 고정해 decode한 다음 DB에서 현재 사용자를 다시 조회합니다. 삭제된 사용자 token도 이 단계에서 거부됩니다.
6. 자주 하는 실수와 해결법
7. 직접 실습
8. 이해 점검 질문 3개
9. 핵심 요약
MINI QUIZ
0 / 2JWT 로그인과 인증 미들웨어/의존성 미니 퀴즈
선택 즉시 정답과 해설을 확인할 수 있습니다. 결과는 이 브라우저에만 저장됩니다.
LESSON STATUS
22강. JWT 로그인과 인증 미들웨어/의존성 미완료 상태학습을 마쳤나요?
직접 실습과 점검 질문까지 확인한 뒤 완료로 표시하세요.