package user import ( "fmt" model "github.com/cloudreve/Cloudreve/v3/models" "github.com/cloudreve/Cloudreve/v3/pkg/cache" "github.com/cloudreve/Cloudreve/v3/pkg/email" "github.com/cloudreve/Cloudreve/v3/pkg/hashid" "github.com/cloudreve/Cloudreve/v3/pkg/serializer" "github.com/cloudreve/Cloudreve/v3/pkg/util" "github.com/gin-gonic/gin" "github.com/pquerna/otp/totp" "net/url" ) // UserLoginService 管理用户登录的服务 type UserLoginService struct { //TODO 细致调整验证规则 UserName string `form:"userName" json:"userName" binding:"required,email"` Password string `form:"Password" json:"Password" binding:"required,min=4,max=64"` } // UserResetEmailService 发送密码重设邮件服务 type UserResetEmailService struct { UserName string `form:"userName" json:"userName" binding:"required,email"` } // UserResetService 密码重设服务 type UserResetService struct { Password string `form:"Password" json:"Password" binding:"required,min=4,max=64"` ID string `json:"id" binding:"required"` Secret string `json:"secret" binding:"required"` } // Reset 重设密码 func (service *UserResetService) Reset(c *gin.Context) serializer.Response { // 取得原始用户ID uid, err := hashid.DecodeHashID(service.ID, hashid.UserID) if err != nil { return serializer.Err(serializer.CodeNotFound, "重设链接无效", err) } // 检查重设会话 resetSession, exist := cache.Get(fmt.Sprintf("user_reset_%d", uid)) if !exist || resetSession.(string) != service.Secret { return serializer.Err(serializer.CodeNotFound, "链接已过期", err) } // 重设用户密码 user, err := model.GetActiveUserByID(uid) if err != nil { return serializer.Err(serializer.CodeNotFound, "用户不存在", err) } user.SetPassword(service.Password) if err := user.Update(map[string]interface{}{"password": user.Password}); err != nil { return serializer.DBErr("无法重设密码", err) } cache.Deletes([]string{fmt.Sprintf("%d", uid)}, "user_reset_") return serializer.Response{} } // Reset 发送密码重设邮件 func (service *UserResetEmailService) Reset(c *gin.Context) serializer.Response { // 查找用户 if user, err := model.GetUserByEmail(service.UserName); err == nil { if user.Status == model.Baned || user.Status == model.OveruseBaned { return serializer.Err(403, "该账号已被封禁", nil) } if user.Status == model.NotActivicated { return serializer.Err(403, "该账号未激活", nil) } // 创建密码重设会话 secret := util.RandStringRunes(32) cache.Set(fmt.Sprintf("user_reset_%d", user.ID), secret, 3600) // 生成用户访问的重设链接 controller, _ := url.Parse("/reset") finalURL := model.GetSiteURL().ResolveReference(controller) queries := finalURL.Query() queries.Add("id", hashid.HashID(user.ID, hashid.UserID)) queries.Add("sign", secret) finalURL.RawQuery = queries.Encode() // 发送密码重设邮件 title, body := email.NewResetEmail(user.Nick, finalURL.String()) if err := email.Send(user.Email, title, body); err != nil { return serializer.Err(serializer.CodeInternalSetting, "无法发送密码重设邮件", err) } } return serializer.Response{} } // Login 二步验证继续登录 func (service *Enable2FA) Login(c *gin.Context) serializer.Response { if uid, ok := util.GetSession(c, "2fa_user_id").(uint); ok { // 查找用户 expectedUser, err := model.GetActiveUserByID(uid) if err != nil { return serializer.Err(serializer.CodeNotFound, "用户不存在", nil) } // 验证二步验证代码 if !totp.Validate(service.Code, expectedUser.TwoFactor) { return serializer.ParamErr("验证代码不正确", nil) } //登陆成功,清空并设置session util.DeleteSession(c, "2fa_user_id") util.SetSession(c, map[string]interface{}{ "user_id": expectedUser.ID, }) return serializer.BuildUserResponse(expectedUser) } return serializer.Err(serializer.CodeNotFound, "登录会话不存在", nil) } // Login 用户登录函数 func (service *UserLoginService) Login(c *gin.Context) serializer.Response { expectedUser, err := model.GetUserByEmail(service.UserName) // 一系列校验 if err != nil { return serializer.Err(serializer.CodeCredentialInvalid, "用户邮箱或密码错误", err) } if authOK, _ := expectedUser.CheckPassword(service.Password); !authOK { return serializer.Err(serializer.CodeCredentialInvalid, "用户邮箱或密码错误", nil) } if expectedUser.Status == model.Baned || expectedUser.Status == model.OveruseBaned { return serializer.Err(403, "该账号已被封禁", nil) } if expectedUser.Status == model.NotActivicated { return serializer.Err(403, "该账号未激活", nil) } if expectedUser.TwoFactor != "" { // 需要二步验证 util.SetSession(c, map[string]interface{}{ "2fa_user_id": expectedUser.ID, }) return serializer.Response{Code: 203} } //登陆成功,清空并设置session util.SetSession(c, map[string]interface{}{ "user_id": expectedUser.ID, }) return serializer.BuildUserResponse(expectedUser) }