// Filename: internal/middleware/cors.go package middleware import ( "net/http" "strings" "github.com/gin-gonic/gin" ) type CORSConfig struct { AllowedOrigins []string AllowedMethods []string AllowedHeaders []string ExposedHeaders []string AllowCredentials bool MaxAge int } func CORSMiddleware(config CORSConfig) gin.HandlerFunc { return func(c *gin.Context) { origin := c.Request.Header.Get("Origin") // 检查 origin 是否允许 if origin != "" && isOriginAllowed(origin, config.AllowedOrigins) { c.Writer.Header().Set("Access-Control-Allow-Origin", origin) } if config.AllowCredentials { c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") } if len(config.ExposedHeaders) > 0 { c.Writer.Header().Set("Access-Control-Expose-Headers", strings.Join(config.ExposedHeaders, ", ")) } // 处理预检请求 if c.Request.Method == http.MethodOptions { if len(config.AllowedMethods) > 0 { c.Writer.Header().Set("Access-Control-Allow-Methods", strings.Join(config.AllowedMethods, ", ")) } if len(config.AllowedHeaders) > 0 { c.Writer.Header().Set("Access-Control-Allow-Headers", strings.Join(config.AllowedHeaders, ", ")) } if config.MaxAge > 0 { c.Writer.Header().Set("Access-Control-Max-Age", string(rune(config.MaxAge))) } c.AbortWithStatus(http.StatusNoContent) return } c.Next() } } func isOriginAllowed(origin string, allowedOrigins []string) bool { for _, allowed := range allowedOrigins { if allowed == "*" || allowed == origin { return true } // 支持通配符子域名 if strings.HasPrefix(allowed, "*.") { domain := strings.TrimPrefix(allowed, "*.") if strings.HasSuffix(origin, domain) { return true } } } return false } // 使用示例 func SetupCORS(r *gin.Engine) { r.Use(CORSMiddleware(CORSConfig{ AllowedOrigins: []string{"https://yourdomain.com", "*.yourdomain.com"}, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Authorization", "Content-Type", "X-Api-Key"}, ExposedHeaders: []string{"X-Request-Id"}, AllowCredentials: true, MaxAge: 3600, })) }