feat: 添加密码重置功能

- 后端: 新增 POST /auth/reset-password 端点(邮箱+验证码+新密码)
- 后端: 新增 ResetPasswordRequest schema
- 前端: 新增 /forgot-password 页面(分步骤:输入邮箱→验证码+新密码→完成)
- 前端: 登录页添加"忘记密码?"链接

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Your Name
2026-02-09 19:00:42 +08:00
co-authored by Claude Opus 4.6
parent 45c6c034e0
commit 3a6e25b5b1
5 changed files with 343 additions and 6 deletions
+43
View File
@@ -13,6 +13,7 @@ from app.schemas.auth import (
LoginResponse,
RefreshTokenRequest,
RefreshTokenResponse,
ResetPasswordRequest,
SendEmailCodeRequest,
UserResponse,
)
@@ -27,6 +28,7 @@ from app.services.auth import (
update_refresh_token,
decode_token,
get_user_organization_info,
hash_password,
)
from app.services.verification import generate_code, verify_code
from app.services.email import send_verification_email
@@ -323,6 +325,47 @@ async def refresh_token(
return RefreshTokenResponse(access_token=access_token)
@router.post("/reset-password")
async def reset_password(
request: ResetPasswordRequest,
req: Request,
db: AsyncSession = Depends(get_db),
):
"""
重置密码
- 需要先调用 /auth/send-code (purpose=reset_password) 获取验证码
- 验证码正确后设置新密码
"""
# 验证验证码
if not verify_code(request.email, request.email_code, "reset_password"):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="验证码错误或已过期",
)
# 查找用户
user = await get_user_by_email(db, request.email)
if not user:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="该邮箱未注册",
)
# 更新密码
user.password_hash = hash_password(request.new_password)
# 审计日志
await log_action(
db, "reset_password", "user", user.id, user.id,
user.name, user.role.value,
ip_address=req.client.host if req.client else None,
)
await db.commit()
return {"message": "密码已重置,请使用新密码登录"}
@router.post("/logout")
async def logout(
req: Request,
+16
View File
@@ -81,6 +81,22 @@ class BindEmailRequest(BaseModel):
password: str = Field(..., min_length=6, max_length=128)
class ResetPasswordRequest(BaseModel):
"""重置密码请求(通过邮箱验证码)"""
email: EmailStr
email_code: str = Field(..., min_length=4, max_length=8)
new_password: str = Field(..., min_length=6, max_length=128)
class Config:
json_schema_extra = {
"example": {
"email": "user@example.com",
"email_code": "123456",
"new_password": "newpassword123"
}
}
class ChangePasswordRequest(BaseModel):
"""修改密码请求"""
old_password: str