package middleware import ( "errors" "net/http" "strings" "billai-server/config" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) // Claims JWT claims (duplicated here to avoid cross-package import from handler). type Claims struct { Username string `json:"username"` Name string `json:"name"` Role string `json:"role"` jwt.RegisteredClaims } func AuthRequired() gin.HandlerFunc { return func(c *gin.Context) { tokenString := c.GetHeader("Authorization") if tokenString == "" { c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "error": "未提供 Token", "code": "TOKEN_MISSING", }) c.Abort() return } if strings.HasPrefix(tokenString, "Bearer ") { tokenString = strings.TrimPrefix(tokenString, "Bearer ") } secret := config.Global.JWTSecret if secret == "" { secret = "billai-default-secret" } token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }, jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Name})) if err != nil || !token.Valid { code := "TOKEN_INVALID" message := "Token 无效" if err != nil && errors.Is(err, jwt.ErrTokenExpired) { code = "TOKEN_EXPIRED" message = "Token 已过期" } c.JSON(http.StatusUnauthorized, gin.H{ "success": false, "error": message, "code": code, }) c.Abort() return } if claims, ok := token.Claims.(*Claims); ok { c.Set("user", gin.H{ "username": claims.Username, "name": claims.Name, "role": claims.Role, }) } c.Next() } }