jdysya 0edabc54b0 feat(auth): 添加忘记密码功能
- 新增忘记密码表单页面和处理逻辑
- 实现用户通过地区、姓名和手机号找回密码的功能
- 添加密码重置成功后的提示信息
- 优化登录页面,增加忘记密码链接
2025-07-04 19:32:09 +08:00

254 lines
6.3 KiB
Go

package handlers
import (
"net/http"
"strconv"
"strings"
"gateway/config"
"gateway/models"
"gateway/utils"
"github.com/gin-contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
"golang.org/x/crypto/bcrypt"
)
func GetLogin(c *gin.Context) {
returnURL := c.Query("return_url")
c.HTML(http.StatusOK, "login.html", gin.H{
"return_url": returnURL,
})
}
func PostLogin(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
returnURL := c.PostForm("return_url")
var user models.User
if err := db.Where("mobile = ?", username).First(&user).Error; err != nil {
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"error": "用户不存在或密码错误",
"return_url": returnURL,
})
return
}
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
c.HTML(http.StatusUnauthorized, "login.html", gin.H{
"error": "用户不存在或密码错误",
"return_url": returnURL,
})
return
}
session := sessions.Default(c)
session.Set("user", user.ID)
if err := session.Save(); err != nil {
utils.Logger.Errorf("Session保存失败: %v", err)
c.HTML(http.StatusInternalServerError, "login.html", gin.H{
"error": "登录状态保存失败",
"return_url": returnURL,
})
return
}
if returnURL != "" {
c.Redirect(http.StatusSeeOther, returnURL)
return
}
c.Redirect(http.StatusSeeOther, "/")
}
}
func GetRegister(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
regions, err := getRegions(db)
if err != nil {
c.HTML(http.StatusInternalServerError, "register.html", gin.H{"error": "系统错误"})
return
}
c.HTML(http.StatusOK, "register.html", gin.H{"regions": regions})
}
}
func PostRegister(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
regionStr, exists := c.GetPostForm("region")
if !exists {
handleRegisterError(c, db, "请选择所在地区")
return
}
regionID, err := strconv.ParseUint(regionStr, 10, 32)
if err != nil {
handleRegisterError(c, db, "无效的地区参数")
return
}
user := models.User{
FullName: c.PostForm("fullname"),
Mobile: c.PostForm("mobile"),
RegionID: uint(regionID),
}
var region models.Region
if err := db.First(&region, user.RegionID).Error; err != nil {
handleRegisterError(c, db, "请选择有效地区")
return
}
if len(user.Mobile) != 11 {
handleRegisterError(c, db, "手机号格式不正确")
return
}
password := c.PostForm("password")
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
utils.Logger.Errorf("密码加密失败: %v", err)
handleRegisterError(c, db, "注册失败")
return
}
user.Password = string(hashedPassword)
if err := db.Create(&user).Error; err != nil {
utils.Logger.Errorf("用户创建失败: %v", err)
errorMsg := "注册失败"
if strings.Contains(err.Error(), "UNIQUE constraint failed") {
errorMsg = "该手机号已注册"
}
handleRegisterError(c, db, errorMsg)
return
}
c.Redirect(http.StatusSeeOther, "/login")
}
}
func Logout(c *gin.Context) {
session := sessions.Default(c)
// 使用配置中的session选项
session.Options(config.GetLogoutSessionOptions())
// 清除session数据
session.Clear()
// 添加清除缓存的 HTTP 头
c.Header("Cache-Control", "no-cache, no-store, must-revalidate")
c.Header("Pragma", "no-cache")
c.Header("Expires", "0")
// 保存更改
if err := session.Save(); err != nil {
utils.Logger.Errorf("退出登录失败: %v", err)
c.HTML(http.StatusInternalServerError, "error.html", gin.H{"error": "退出登录失败"})
return
}
c.Redirect(http.StatusSeeOther, "/login")
}
func getRegions(db *gorm.DB) ([]models.Region, error) {
var regions []models.Region
if err := db.Find(&regions).Error; err != nil {
utils.Logger.Errorf("获取地区数据失败: %v", err)
return nil, err
}
return regions, nil
}
func handleRegisterError(c *gin.Context, db *gorm.DB, errorMessage string) {
regions, err := getRegions(db)
if err != nil {
c.HTML(http.StatusInternalServerError, "register.html", gin.H{"error": "系统错误"})
return
}
c.HTML(http.StatusBadRequest, "register.html", gin.H{
"error": errorMessage,
"regions": regions,
"form": gin.H{
"fullname": c.PostForm("fullname"),
"mobile": c.PostForm("mobile"),
},
})
}
func GetUserInfo(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
session := sessions.Default(c)
userID := session.Get("user")
if userID == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
var user models.User
if err := db.First(&user, userID).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "获取用户信息失败"})
return
}
c.JSON(http.StatusOK, gin.H{
"id": user.ID,
"fullName": user.FullName,
"mobile": user.Mobile,
})
}
}
// 忘记密码表单页面
func GetForgotPassword(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
regions, _ := getRegions(db)
c.HTML(200, "forgot_password.html", gin.H{
"regions": regions,
"error": "",
})
}
}
// 处理忘记密码表单提交
func PostForgotPassword(db *gorm.DB) gin.HandlerFunc {
return func(c *gin.Context) {
regionID := c.PostForm("region")
fullname := c.PostForm("fullname")
mobile := c.PostForm("mobile")
password := c.PostForm("password")
confirmPassword := c.PostForm("confirmPassword")
if password != confirmPassword {
c.HTML(200, "login.html", gin.H{
"error": "两次输入的密码不一致",
})
return
}
var user models.User
dbErr := db.Where("region_id = ? AND full_name = ? AND mobile = ?", regionID, fullname, mobile).First(&user).Error
if dbErr != nil {
c.HTML(200, "login.html", gin.H{
"error": "用户信息不正确,请检查地区、姓名和手机号",
})
return
}
hash, hashErr := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if hashErr != nil {
c.HTML(200, "login.html", gin.H{
"error": "密码加密失败,请重试",
})
return
}
user.Password = string(hash)
db.Save(&user)
c.HTML(200, "login.html", gin.H{
"error": "密码重置成功,请用新密码登录",
})
}
}