기술과 산업/언어 및 프레임워크
FastAPI 시리즈 12화 - JWT 기반 인증 시스템 완성: 토큰 구조와 보안 처리 심화
B컷개발자
2025. 5. 30. 01:20
728x90
FastAPI에서 JWT(Json Web Token)의 구조, 만료 처리, 리프레시 토큰 전략, 서명 보안 방식 등을 실전 중심으로 다룹니다. 인증 시스템을 안전하게 완성하는 방법을 소개합니다.
왜 JWT 구조를 이해해야 하는가?
JWT(Json Web Token)는 다음과 같은 구조로 구성됩니다:
HEADER.PAYLOAD.SIGNATURE
파트설명
Header | 알고리즘 및 타입 (alg, typ) |
Payload | 사용자 데이터 (sub, exp, role 등) |
Signature | 서버 비밀키로 서명된 무결성 보장 부분 |
✅ JWT는 읽을 수 있지만 조작할 수 없어야 합니다.
따라서 서명(Signature)을 검증하지 않으면, 누구나 위조된 토큰을 만들 수 있게 됩니다.
1. JWT 유효성 검증 강화
from jose import jwt, JWTError
from fastapi import HTTPException
def verify_token(token: str):
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except JWTError:
raise HTTPException(status_code=401, detail="Invalid token")
- jwt.decode()는 signature 검증 + 만료 확인까지 자동 수행
- JWTError로 모든 오류 포착 가능
- signature가 일치하지 않으면 인증 실패
2. 토큰 만료 시간 처리 (exp)
from datetime import datetime, timedelta
def create_access_token(data: dict, expires_delta: 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)
- exp 필드는 ISO UTC 기준으로 입력
- Swagger에서 만료된 토큰은 즉시 401 반환됨
3. 리프레시 토큰 전략 설계
📌 왜 필요한가?
- 액세스 토큰은 수명이 짧아야 보안에 유리 (15~30분)
- 매번 로그인할 수 없기 때문에 장기 보관용 리프레시 토큰이 필요
📌 리프레시 토큰 흐름
1. 로그인 성공 → access_token + refresh_token 발급
2. access_token 만료 시 → refresh_token으로 재발급 요청
3. refresh_token은 DB나 Redis에서 상태 관리
4. 리프레시 토큰 발급 예제
def create_refresh_token(data: dict):
expire = datetime.utcnow() + timedelta(days=7)
data.update({"exp": expire})
return jwt.encode(data, SECRET_KEY, algorithm=ALGORITHM)
@app.post("/token/refresh")
def refresh_token(refresh_token: str):
try:
payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
username = payload.get("sub")
# 추가 검증: DB에서 refresh_token 유효성 확인 필요
new_access = create_access_token({"sub": username})
return {"access_token": new_access}
except JWTError:
raise HTTPException(status_code=401, detail="Invalid refresh token")
5. 실무 보안 강화 포인트
항목설명
비밀번호 해싱 | bcrypt 활용 (pip install passlib[bcrypt]) |
토큰 블랙리스트 | 로그아웃 시 refresh_token 무효화 (DB나 Redis 활용) |
HTTPS 적용 | 토큰 유출 방지를 위해 항상 TLS 사용 |
secure, httponly 쿠키 | 토큰을 쿠키에 저장 시 필수 설정 |
사용자 역할(Role) 기반 제어 | JWT Payload에 role 포함 후 접근 제어 적용 |
6. 사용자 권한(Role) 기반 API 접근 제어
def get_admin_user(token: str = Depends(oauth2_scheme)):
payload = verify_token(token)
if payload.get("role") != "admin":
raise HTTPException(status_code=403, detail="Admins only")
return payload
@app.get("/admin/dashboard")
def read_admin_dashboard(user=Depends(get_admin_user)):
return {"message": f"Welcome {user['sub']}, to the admin dashboard"}
- JWT에 사용자 role 정보를 포함시키고
- 인증 함수에서 권한 확인 로직을 분리
FastAPI 인증 시스템을 진짜 서비스 수준으로 완성하려면
항목적용 방법
JWT 발급 | sub, exp 포함 후 서명 |
토큰 만료 | timedelta 기반 설정 |
리프레시 토큰 | 별도 토큰 발급 + 상태 관리 |
사용자 권한 제어 | JWT에 role 필드 포함 |
보안 강화 | HTTPS, bcrypt, 토큰 저장소, 쿠키 옵션 등 적용 필요 |
FastAPI는 인증을 “쉽게 시작할 수 있으면서도, 깊이 있게 확장 가능한 구조”로 설계되어 있습니다.
이제 로그인 기능을 넘어서 서비스 전반의 인증/보안 흐름을 스스로 구성할 수 있는 수준까지 도달한 것입니다.
다음 글 예고
FastAPI 시리즈 13화 - 사용자 권한(Role) 관리와 종속성 적용 방법
에서는 사용자 계층별 접근 제어를 어떻게 분리하고, Depends를 활용해 권한 시스템을 구현하는지를 설명합니다.
728x90