mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
feat(auth): implement password reset with Google Authenticator verification (#537)
实现忘记密码功能,用户可以通过邮箱和Google Authenticator验证码重置密码。 **后端改动:** - 添加 `/api/reset-password` 接口 - 实现 `UpdateUserPassword` 数据库方法 - 验证邮箱、OTP和新密码 **前端改动:** - 新增 `ResetPasswordPage` 组件 - 在登录页面添加"忘记密码"链接 - 实现密码重置表单(新密码、确认密码、OTP验证) - 添加密码可见性切换功能 - 支持中英文国际化 **安全特性:** - 要求Google Authenticator验证 - 密码强度验证(最少6位) - 密码确认匹配检查 - 密码哈希存储 Co-authored-by: tinkle-community <tinklefund@gmail.com>
This commit is contained in:
@@ -79,6 +79,7 @@ func (s *Server) setupRoutes() {
|
||||
api.POST("/login", s.handleLogin)
|
||||
api.POST("/verify-otp", s.handleVerifyOTP)
|
||||
api.POST("/complete-registration", s.handleCompleteRegistration)
|
||||
api.POST("/reset-password", s.handleResetPassword)
|
||||
|
||||
// 系统支持的模型和交易所(无需认证)
|
||||
api.GET("/supported-models", s.handleGetSupportedModels)
|
||||
@@ -1728,6 +1729,50 @@ func (s *Server) handleVerifyOTP(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleResetPassword 重置密码(通过邮箱 + OTP 验证)
|
||||
func (s *Server) handleResetPassword(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=6"`
|
||||
OTPCode string `json:"otp_code" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 查询用户
|
||||
user, err := s.database.GetUserByEmail(req.Email)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "邮箱不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证 OTP
|
||||
if !auth.VerifyOTP(user.OTPSecret, req.OTPCode) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Google Authenticator 验证码错误"})
|
||||
return
|
||||
}
|
||||
|
||||
// 生成新密码哈希
|
||||
newPasswordHash, err := auth.HashPassword(req.NewPassword)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码处理失败"})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新密码
|
||||
err = s.database.UpdateUserPassword(user.ID, newPasswordHash)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "密码更新失败"})
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("✓ 用户 %s 密码已重置", user.Email)
|
||||
c.JSON(http.StatusOK, gin.H{"message": "密码重置成功,请使用新密码登录"})
|
||||
}
|
||||
|
||||
// initUserDefaultConfigs 为新用户初始化默认的模型和交易所配置
|
||||
func (s *Server) initUserDefaultConfigs(userID string) error {
|
||||
// 注释掉自动创建默认配置,让用户手动添加
|
||||
|
||||
Reference in New Issue
Block a user