Feat: 강력한 비밀번호 정책 및 로그인 보안 강화

- 비밀번호 최소 10자 이상, 특수문자 1개 이상 필수
- 20회 로그인 실패 시 비밀번호 재설정 필요
- 로그인 페이지에 남은 시도 횟수 경고 표시
- 계정 잠금 시 비밀번호 재설정 링크 제공
- 회원가입 페이지에 비밀번호 요구사항 체크리스트 UI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
AutonetSellCar Deploy
2026-01-01 19:19:36 +09:00
parent 1e3ad2fa65
commit 9853f0b4d5
6 changed files with 169 additions and 11 deletions

View File

@@ -161,7 +161,41 @@ def login(
):
"""로그인"""
user = db.query(User).filter(User.email == form_data.username).first()
if not user or not verify_password(form_data.password, user.password_hash):
# 사용자가 존재하는 경우 로그인 실패 처리
if user:
# 비밀번호 재설정 필요 여부 체크
if getattr(user, 'password_reset_required', False):
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Password reset required. Too many failed login attempts.",
headers={"WWW-Authenticate": "Bearer"},
)
# 비밀번호 검증
if not verify_password(form_data.password, user.password_hash):
# 실패 횟수 증가
failed_attempts = getattr(user, 'failed_login_attempts', 0) or 0
user.failed_login_attempts = failed_attempts + 1
# 20회 이상 실패 시 비밀번호 재설정 필요
if user.failed_login_attempts >= 20:
user.password_reset_required = True
db.commit()
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Account locked. Password reset required due to too many failed attempts.",
headers={"WWW-Authenticate": "Bearer"},
)
db.commit()
remaining = 20 - user.failed_login_attempts
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=f"Incorrect email or password. {remaining} attempts remaining before account lock.",
headers={"WWW-Authenticate": "Bearer"},
)
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
@@ -184,6 +218,10 @@ def login(
headers={"WWW-Authenticate": "Bearer"},
)
# 로그인 성공 - 실패 횟수 초기화
user.failed_login_attempts = 0
db.commit()
access_token = create_access_token(data={"sub": user.email})
return Token(access_token=access_token)