한양과학기술고등학교

에듀테크 프로젝트 대시보드

JWT 인증 구현

JWT PyJWT passlib bcrypt exp refresh

비밀번호는 해시로 저장하고, 로그인하면 서명된 토큰(JWT)을 발급합니다.

1. 설치

pip install pyjwt "passlib[bcrypt]"
# passlib 대신 bcrypt 직접 사용도 가능

2. 비밀번호 해시

from passlib.context import CryptContext

pwd = CryptContext(schemes=["bcrypt"], deprecated="auto")

def hash_password(raw: str) -> str:
    return pwd.hash(raw)

def verify_password(raw: str, hashed: str) -> bool:
    return pwd.verify(raw, hashed)
절대 금지
  • 평문 저장
  • 역산 가능한 암호화(양방향)
  • MD5/SHA1 단순 해시 — bcrypt/argon2 를 쓸 것

3. 토큰 생성·검증

from datetime import datetime, timedelta, timezone
import jwt   # PyJWT

SECRET = settings.secret_key
ALGO = "HS256"

def create_access_token(sub: str, minutes: int = 30) -> str:
    payload = {
        "sub": sub,
        "exp": datetime.now(timezone.utc) + timedelta(minutes=minutes),
        "iat": datetime.now(timezone.utc),
    }
    return jwt.encode(payload, SECRET, algorithm=ALGO)

def verify_token(token: str) -> str | None:
    try:
        payload = jwt.decode(token, SECRET, algorithms=[ALGO])
        return payload.get("sub")
    except jwt.PyJWTError:
        return None

4. 로그인 → 현재 사용자

@app.post("/token")
def login(form: Annotated[OAuth2PasswordRequestForm, Depends()], db: DbDep):
    user = db.scalar(select(User).where(User.username == form.username))
    if not user or not verify_password(form.password, user.hashed_password):
        raise HTTPException(400, "아이디 또는 비밀번호 오류")
    return {"access_token": create_access_token(user.username), "token_type": "bearer"}

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)], db: DbDep) -> User:
    sub = verify_token(token)
    user = db.scalar(select(User).where(User.username == sub)) if sub else None
    if user is None:
        raise HTTPException(401, headers={"WWW-Authenticate": "Bearer"})
    return user

5. 액세스 / 리프레시 토큰

권장 패턴
  • 액세스 토큰: 수명 짧게(15~30분), API 인증에 사용
  • 리프레시 토큰: 수명 길게(며칠~수주), DB 에 저장하고 폐기(revoke) 가능하게
  • JWT 는 서버가 개별 무효화하기 어려움 — 로그아웃/차단이 중요하면 세션 방식(이 서버 lms/jobhanyang) 이나 토큰 블랙리스트 고려
"명시적 의존성 주입은 테스트를 쉽게 만든다"