에듀테크 프로젝트 대시보드
JWT 인증 구현
비밀번호는 해시로 저장하고, 로그인하면 서명된 토큰(JWT)을 발급합니다.
pip install pyjwt "passlib[bcrypt]"
# passlib 대신 bcrypt 직접 사용도 가능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)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@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