diff --git a/backend/internal/auth/middleware.go b/backend/internal/auth/middleware.go new file mode 100644 index 0000000..a932240 --- /dev/null +++ b/backend/internal/auth/middleware.go @@ -0,0 +1,54 @@ +package auth + +import ( + "net/http" + "strings" + + "github.com/gin-gonic/gin" +) + +// contextKey 用于在 Gin context 中存储 Claims 的 key。 +const ( + ContextKeyUserID = "user_id" + ContextKeyUsername = "username" +) + +// AuthMiddleware 返回 Gin 中间件,从 Authorization: Bearer 提取并校验 JWT。 +// 校验成功后将 user_id 和 username 写入 Gin Context。 +func AuthMiddleware(tokenMgr *TokenManager) gin.HandlerFunc { + return func(c *gin.Context) { + authHeader := c.GetHeader("Authorization") + if authHeader == "" { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "code": "INVALID_TOKEN", + "message": "missing authorization header", + }) + return + } + + // 提取 Bearer token + parts := strings.SplitN(authHeader, " ", 2) + if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "code": "INVALID_TOKEN", + "message": "invalid authorization format", + }) + return + } + + claims, err := tokenMgr.ValidateAccess(parts[1]) + if err != nil { + c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ + "code": "INVALID_TOKEN", + "message": "invalid or expired token", + }) + return + } + + // 将用户信息写入 context + c.Set(ContextKeyUserID, claims.UserID) + c.Set(ContextKeyUsername, claims.Username) + + c.Next() + } +}