From 395f1f681d52daaea5a1aa2411ffb9f742f647d4 Mon Sep 17 00:00:00 2001 From: junleea <354425203@qq.com> Date: Tue, 18 Mar 2025 14:00:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=94=A8=E6=88=B7=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E9=87=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handler/user.go | 108 +++++++++++++++++++++++++++++++++++++++++ proto/conf.go | 2 +- proto/user_req.go | 7 +++ service/userService.go | 33 +++++++++++++ worker/tool.go | 13 +++++ 5 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 worker/tool.go diff --git a/handler/user.go b/handler/user.go index 0c40dcb..4cc3a24 100644 --- a/handler/user.go +++ b/handler/user.go @@ -28,6 +28,7 @@ func SetUpUserGroup(router *gin.Engine) { userGroup.POST("/update", UpdateUserInfo) userGroup.POST("/sync", GetSyncUserInfo) userGroup.POST("/delete", DeleteUser) + userGroup.POST("/reset", ResetPassword) } type RLReq struct { @@ -415,3 +416,110 @@ func GetSyncUserInfo(c *gin.Context) { return } } + +type ResetPasswordReq struct { + Email string `json:"email" form:"email"` + OldPassword string `json:"old_password" form:"old_password"` + NewPassword string `json:"new_password" form:"new_password"` + Type int `json:"type" form:"type"` //0获取验证码,2为邮箱验证码重置密码,1为旧密码重置密码 + Code string `json:"code" form:"code"` //验证码 +} + +func ResetPassword(c *gin.Context) { + var req_data ResetPasswordReq + if err := c.ShouldBind(&req_data); err == nil { + if req_data.Type == 0 { + //获取验证码 + //查看是否存在该邮箱 + user := dao.FindUserByEmail(req_data.Email) + if user.ID == 0 { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "邮箱不存在", "data": "2"}) + return + } + if worker.IsContainKey("reset_password_" + req_data.Email) { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "验证码已发送,请5分钟后再试", "data": "2"}) + return + } + //随机字符串验证码大写 + code := worker.GetRandomString(6) + worker.SetRedisWithExpire("reset_password_"+req_data.Email, code, time.Minute*5) //设置5分钟过期` + //发送邮件 + service.SendEmail(req_data.Email, "大学生学业作品AI生成工具开发重置密码", "验证码:"+code+" ,请在5分钟内使用!") + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": "2"}) + return + } else if req_data.Type == 1 { + //旧密码重置密码 + if len(req_data.OldPassword) != 32 { + hasher := md5.New() + hasher.Write([]byte(req_data.OldPassword)) // 生成密码的 MD5 散列值 + req_data.OldPassword = hex.EncodeToString(hasher.Sum(nil)) // 生成密码的 MD5 散列值 + } + if len(req_data.NewPassword) != 32 { + hasher := md5.New() + hasher.Write([]byte(req_data.NewPassword)) // 生成密码的 MD5 散列值 + req_data.NewPassword = hex.EncodeToString(hasher.Sum(nil)) // 生成密码的 MD5 散列值 + } + user := dao.FindUserByEmail(req_data.Email) + if user.ID == 0 { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "邮箱不存在", "data": "2"}) + return + } + if user.Password != req_data.OldPassword { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "旧密码错误", "data": "2"}) + return + } + if user.Password == req_data.NewPassword { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "新旧密码相同", "data": "2"}) + return + } + dao.UpdateUserByID(int(user.ID), user.Name, req_data.NewPassword, user.Email) + var resp proto.ResponseOAuth + token, err2 := service.CreateTokenAndSave(user) + if err2 != nil { + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "new token error", "data": resp}) + return + } + resp.Token = token + resp.ID = user.ID + resp.Name = user.Name + resp.Email = user.Email + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": resp}) + } else if req_data.Type == 2 { + //邮箱重置密码 + if len(req_data.NewPassword) != 32 { + hasher := md5.New() + hasher.Write([]byte(req_data.NewPassword)) // 生成密码的 MD5 散列值 + req_data.NewPassword = hex.EncodeToString(hasher.Sum(nil)) // 生成密码的 MD5 散列值 + } + user := dao.FindUserByEmail(req_data.Email) + if user.ID == 0 { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "邮箱不存在", "data": "2"}) + return + } + code := worker.GetRedis("reset_password_" + req_data.Email) + if code != req_data.Code { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "验证码错误", "data": "2"}) + return + } + dao.UpdateUserByID(int(user.ID), user.Name, req_data.NewPassword, user.Email) + token, err2 := service.CreateTokenAndSave(user) + if err2 != nil { + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "new token error", "data": "2"}) + return + } + var resp proto.ResponseOAuth + resp.Token = token + resp.ID = user.ID + resp.Name = user.Name + resp.Email = user.Email + c.JSON(200, gin.H{"code": proto.SuccessCode, "message": "success", "data": resp}) + } else { + c.JSON(200, gin.H{"code": proto.OperationFailed, "message": "type error", "data": "2"}) + return + } + + } else { + c.JSON(200, gin.H{"code": proto.ParameterError, "message": err, "data": "2"}) + return + } +} diff --git a/proto/conf.go b/proto/conf.go index e6ed1c3..0c6bb27 100644 --- a/proto/conf.go +++ b/proto/conf.go @@ -9,7 +9,7 @@ import ( var Config ConfigStruct var SigningKey = []byte{} -var Url_map = map[string]bool{"/login": true, "/register": true, "/uuid": true, "/gqr": true, "/cid/callback": true, "/tool/monitor": true, "/user/sync": true, "/tool/file/": true} // 不需要token验证的url +var Url_map = map[string]bool{"/login": true, "/register": true, "/uuid": true, "/gqr": true, "/cid/callback": true, "/tool/monitor": true, "/user/sync": true, "/tool/file/": true, "/user/reset": true} // 不需要token验证的url var Per_menu_map = map[string]int{"/video/": 1, "/device/": 2, "/cid/": 3} var File_Type = map[string]int{"im": 1, "avatar": 2, "file": 3, "config": 4} // 文件类型 const ( diff --git a/proto/user_req.go b/proto/user_req.go index 61875d6..84af5b1 100644 --- a/proto/user_req.go +++ b/proto/user_req.go @@ -110,3 +110,10 @@ type UpdateShellRespV2 struct { ID uint `json:"id" form:"id"` Status int `json:"status" form:"status"` } + +type ResponseOAuth struct { + ID uint `json:"id" form:"id"` + Name string `json:"name" form:"name"` + Email string `json:"email" form:"email"` + Token string `json:"token" form:"token"` +} diff --git a/service/userService.go b/service/userService.go index a7fc352..6cf4e86 100644 --- a/service/userService.go +++ b/service/userService.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/golang-jwt/jwt" "regexp" "strconv" "time" @@ -367,3 +368,35 @@ func ConfirmSyncUserData(device string, data proto.UserSyncConfirm) error { } return err } + +// 生成新的token,存入redis,返回信息 +func CreateTokenAndSave(user dao.User) (string, error) { + var tokenString string + var err error + key := "user_" + user.Name + redis_token := worker.GetRedis(string(key)) + if redis_token == "" { + // 生成 JWT 令牌 + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "username": user.Name, + "id": user.ID, + "exp": time.Now().Add(time.Hour * 10).Unix(), // 令牌过期时间, 10小时后过期 + }) + tokenString, err = token.SignedString(proto.SigningKey) + if err != nil { + return "", err + } + + worker.SetRedisWithExpire("user_"+user.Name, tokenString, time.Hour*10) // 将用户信息存入 + worker.SetRedisWithExpire(tokenString, tokenString, time.Hour*10) // 设置过期时间为10分钟 + data := make(map[string]interface{}) + data["id"] = user.ID + data["username"] = user.Name + data["email"] = user.Email + worker.SetHash(tokenString, data) // 将用户信息存入 + } else { + tokenString = redis_token + } + // 返回令牌 + return tokenString, err +} diff --git a/worker/tool.go b/worker/tool.go new file mode 100644 index 0000000..e9a0b55 --- /dev/null +++ b/worker/tool.go @@ -0,0 +1,13 @@ +package worker + +import "math/rand" + +func GetRandomString(l int) string { + str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + bytes := []byte(str) + var result []byte + for i := 0; i < l; i++ { + result = append(result, bytes[rand.Intn(len(bytes))]) + } + return string(result) +}