diff --git a/backend/internal/handler/auth.go b/backend/internal/handler/auth.go index 4e0bed8..504c9ea 100644 --- a/backend/internal/handler/auth.go +++ b/backend/internal/handler/auth.go @@ -1,183 +1,183 @@ package handler import ( - "net/http" - "time" + "net/http" + "time" - "github.com/gin-gonic/gin" - "github.com/golang-jwt/jwt/v5" - "github.com/rs/zerolog/log" - "golang.org/x/crypto/bcrypt" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "github.com/rs/zerolog/log" + "golang.org/x/crypto/bcrypt" ) type RegisterRequest struct { - Username string `json:"username" binding:"required"` - Email string `json:"email" binding:"required,email"` - Password string `json:"password" binding:"required,min=8"` - Role string `json:"role" binding:"required,oneof=admin editor contributor"` + Username string `json:"username" binding:"required"` + Email string `json:"email" binding:"required,email"` + Password string `json:"password" binding:"required,min=8"` + Role string `json:"role" binding:"required,oneof=admin editor contributor"` } type LoginRequest struct { - Username string `json:"username" binding:"required,min=3,max=32"` - Password string `json:"password" binding:"required"` + Username string `json:"username" binding:"required,min=3,max=32"` + Password string `json:"password" binding:"required"` } type AuthResponse struct { - Token string `json:"token"` + Token string `json:"token"` } func (h *Handler) Register(c *gin.Context) { - // 检查是否启用注册功能 - if !h.config.Auth.Registration.Enabled { - message := h.config.Auth.Registration.Message - if message == "" { - message = "Registration is currently disabled" - } - c.JSON(http.StatusForbidden, gin.H{ - "error": gin.H{ - "code": "REGISTRATION_DISABLED", - "message": message, - }, - }) - return - } + // 检查是否启用注册功能 + if !h.config.Auth.Registration.Enabled { + message := h.config.Auth.Registration.Message + if message == "" { + message = "Registration is currently disabled" + } + c.JSON(http.StatusForbidden, gin.H{ + "error": gin.H{ + "code": "REGISTRATION_DISABLED", + "message": message, + }, + }) + return + } - var req RegisterRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": gin.H{ - "code": "INVALID_REQUEST", - "message": err.Error(), - }, - }) - return - } + var req RegisterRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": gin.H{ + "code": "INVALID_REQUEST", + "message": err.Error(), + }, + }) + return + } - user, err := h.service.CreateUser(c.Request.Context(), req.Username, req.Email, req.Password, req.Role) - if err != nil { - log.Error().Err(err).Msg("Failed to create user") - c.JSON(http.StatusInternalServerError, gin.H{ - "error": gin.H{ - "code": "CREATE_USER_FAILED", - "message": "Failed to create user", - }, - }) - return - } + user, err := h.service.CreateUser(c.Request.Context(), req.Username, req.Email, req.Password, req.Role) + if err != nil { + log.Error().Err(err).Msg("Failed to create user") + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "CREATE_USER_FAILED", + "message": "Failed to create user", + }, + }) + return + } - // Get user roles - roles, err := h.service.GetUserRoles(c.Request.Context(), user.ID) - if err != nil { - log.Error().Err(err).Msg("Failed to get user roles") - c.JSON(http.StatusInternalServerError, gin.H{ - "error": gin.H{ - "code": "GET_ROLES_FAILED", - "message": "Failed to get user roles", - }, - }) - return - } + // Get user roles + roles, err := h.service.GetUserRoles(c.Request.Context(), user.ID) + if err != nil { + log.Error().Err(err).Msg("Failed to get user roles") + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "GET_ROLES_FAILED", + "message": "Failed to get user roles", + }, + }) + return + } - // Extract role names for JWT - roleNames := make([]string, len(roles)) - for i, r := range roles { - roleNames[i] = r.Name - } + // Extract role names for JWT + roleNames := make([]string, len(roles)) + for i, r := range roles { + roleNames[i] = r.Name + } - // Generate JWT token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "sub": user.ID, - "roles": roleNames, - "exp": time.Now().Add(24 * time.Hour).Unix(), - }) + // Generate JWT token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": int64(user.ID), // 将用户 ID 转换为 int64 + "roles": roleNames, + "exp": time.Now().Add(24 * time.Hour).Unix(), + }) - tokenString, err := token.SignedString([]byte(h.cfg.JWT.Secret)) - if err != nil { - log.Error().Err(err).Msg("Failed to generate token") - c.JSON(http.StatusInternalServerError, gin.H{ - "error": gin.H{ - "code": "GENERATE_TOKEN_FAILED", - "message": "Failed to generate token", - }, - }) - return - } + tokenString, err := token.SignedString([]byte(h.cfg.JWT.Secret)) + if err != nil { + log.Error().Err(err).Msg("Failed to generate token") + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "GENERATE_TOKEN_FAILED", + "message": "Failed to generate token", + }, + }) + return + } - c.JSON(http.StatusCreated, AuthResponse{Token: tokenString}) + c.JSON(http.StatusCreated, AuthResponse{Token: tokenString}) } func (h *Handler) Login(c *gin.Context) { - var req LoginRequest - if err := c.ShouldBindJSON(&req); err != nil { - c.JSON(http.StatusBadRequest, gin.H{ - "error": gin.H{ - "code": "INVALID_REQUEST", - "message": err.Error(), - }, - }) - return - } + var req LoginRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "error": gin.H{ + "code": "INVALID_REQUEST", + "message": err.Error(), + }, + }) + return + } - user, err := h.service.GetUserByUsername(c.Request.Context(), req.Username) - if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": gin.H{ - "code": "INVALID_CREDENTIALS", - "message": "Invalid username or password", - }, - }) - return - } + user, err := h.service.GetUserByUsername(c.Request.Context(), req.Username) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": gin.H{ + "code": "INVALID_CREDENTIALS", + "message": "Invalid username or password", + }, + }) + return + } - // 验证密码 - err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) - if err != nil { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": gin.H{ - "code": "INVALID_CREDENTIALS", - "message": "Invalid username or password", - }, - }) - return - } + // 验证密码 + err = bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(req.Password)) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{ + "error": gin.H{ + "code": "INVALID_CREDENTIALS", + "message": "Invalid username or password", + }, + }) + return + } - // Get user roles - roles, err := h.service.GetUserRoles(c.Request.Context(), user.ID) - if err != nil { - log.Error().Err(err).Msg("Failed to get user roles") - c.JSON(http.StatusInternalServerError, gin.H{ - "error": gin.H{ - "code": "GET_ROLES_FAILED", - "message": "Failed to get user roles", - }, - }) - return - } + // Get user roles + roles, err := h.service.GetUserRoles(c.Request.Context(), user.ID) + if err != nil { + log.Error().Err(err).Msg("Failed to get user roles") + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "GET_ROLES_FAILED", + "message": "Failed to get user roles", + }, + }) + return + } - // Extract role names for JWT - roleNames := make([]string, len(roles)) - for i, r := range roles { - roleNames[i] = r.Name - } + // Extract role names for JWT + roleNames := make([]string, len(roles)) + for i, r := range roles { + roleNames[i] = r.Name + } - // Generate JWT token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ - "sub": user.ID, - "roles": roleNames, - "exp": time.Now().Add(24 * time.Hour).Unix(), - }) + // Generate JWT token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ + "sub": int64(user.ID), // 将用户 ID 转换为 int64 + "roles": roleNames, + "exp": time.Now().Add(24 * time.Hour).Unix(), + }) - tokenString, err := token.SignedString([]byte(h.cfg.JWT.Secret)) - if err != nil { - log.Error().Err(err).Msg("Failed to generate token") - c.JSON(http.StatusInternalServerError, gin.H{ - "error": gin.H{ - "code": "GENERATE_TOKEN_FAILED", - "message": "Failed to generate token", - }, - }) - return - } + tokenString, err := token.SignedString([]byte(h.cfg.JWT.Secret)) + if err != nil { + log.Error().Err(err).Msg("Failed to generate token") + c.JSON(http.StatusInternalServerError, gin.H{ + "error": gin.H{ + "code": "GENERATE_TOKEN_FAILED", + "message": "Failed to generate token", + }, + }) + return + } - c.JSON(http.StatusOK, AuthResponse{Token: tokenString}) + c.JSON(http.StatusOK, AuthResponse{Token: tokenString}) } diff --git a/backend/internal/handler/user.go b/backend/internal/handler/user.go index 61637cf..71e7e06 100644 --- a/backend/internal/handler/user.go +++ b/backend/internal/handler/user.go @@ -7,6 +7,7 @@ import ( "github.com/gin-gonic/gin" "github.com/rs/zerolog/log" "tss-rocks-be/internal/types" + "fmt" ) type UpdateCurrentUserRequest struct { @@ -160,24 +161,39 @@ func (h *Handler) DeleteUser(c *gin.Context) { // GetCurrentUser returns the current user's information func (h *Handler) GetCurrentUser(c *gin.Context) { - // 从上下文中获取用户ID(由认证中间件设置) userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) return } + // 将用户ID转换为int64 + var id int64 + switch v := userID.(type) { + case int64: + id = v + case int: + id = int64(v) + case float64: + id = int64(v) + default: + log.Error(). + Str("type", fmt.Sprintf("%T", userID)). + Interface("value", userID). + Msg("Invalid user_id type") + c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid user_id type"}) + return + } + // 获取用户信息 - user, err := h.service.GetUser(c.Request.Context(), userID.(int)) + user, err := h.service.GetUser(c.Request.Context(), int(id)) if err != nil { log.Error().Err(err).Msg("Failed to get user") c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get user information"}) return } - c.JSON(http.StatusOK, gin.H{ - "data": user, - }) + c.JSON(http.StatusOK, user) } // UpdateCurrentUser updates the current user's information diff --git a/backend/internal/middleware/auth.go b/backend/internal/middleware/auth.go index bc88389..2d38058 100644 --- a/backend/internal/middleware/auth.go +++ b/backend/internal/middleware/auth.go @@ -1,7 +1,10 @@ package middleware import ( + "encoding/json" + "fmt" "net/http" + "strconv" "strings" "github.com/gin-gonic/gin" @@ -41,8 +44,61 @@ func AuthMiddleware(jwtSecret string) gin.HandlerFunc { } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - c.Set("user_id", claims["sub"]) - c.Set("user_role", claims["role"]) + // 添加调试信息 + log.Debug().Interface("claims", claims).Msg("Token claims") + + // 获取用户ID + sub, exists := claims["sub"] + if !exists { + log.Error().Msg("Token does not contain sub claim") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"}) + c.Abort() + return + } + + // 打印类型信息 + log.Debug(). + Str("type", fmt.Sprintf("%T", sub)). + Interface("value", sub). + Msg("User ID from token") + + var userID int + switch v := sub.(type) { + case string: + var err error + userID, err = strconv.Atoi(v) + if err != nil { + log.Error().Err(err).Str("sub", v).Msg("Failed to convert string user ID to int") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user ID format"}) + c.Abort() + return + } + case float64: + userID = int(v) + case json.Number: + var err error + userID, err = strconv.Atoi(v.String()) + if err != nil { + log.Error().Err(err).Str("sub", v.String()).Msg("Failed to convert json.Number user ID to int") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user ID format"}) + c.Abort() + return + } + default: + log.Error(). + Str("type", fmt.Sprintf("%T", sub)). + Interface("value", sub). + Msg("Unexpected user ID type") + c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid user ID type"}) + c.Abort() + return + } + + // 将 userID 转换为 int64 以确保类型一致性 + c.Set("user_id", int64(userID)) + if roles, ok := claims["roles"].([]interface{}); ok { + c.Set("user_roles", roles) + } c.Next() } else { c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})