package middleware import ( "net/http" "strings" "cloudnest/internal/pkg/logger" "github.com/gin-gonic/gin" ) // allowedOrigins defines the list of allowed origins for development. // In production, this should be loaded from configuration. // allowedOrigins 定义开发环境允许的源列表。生产环境应从配置加载。 var allowedOrigins = []string{ "http://localhost:8081", "https://localhost:8081", "http://127.0.0.1:8081", "https://127.0.0.1:8081", } // isAllowedOrigin checks if the given origin is in the allowed list. // isAllowedOrigin 检查给定的源是否在允许列表中。 func isAllowedOrigin(origin string) bool { for _, o := range allowedOrigins { if strings.EqualFold(o, origin) { return true } } return false } // CORS creates a middleware that handles Cross-Origin Resource Sharing. // CORS 创建一个处理跨域资源共享(CORS)的中间件。 // // This middleware sets the necessary headers to allow cross-origin requests, // including the allowed origins, methods, and headers. // // Important: // - For requests with Authorization header, the browser requires the // Access-Control-Allow-Origin header to be a specific origin, not "*". // - This middleware dynamically sets the origin to match the request's // Origin header, which allows cross-origin requests with credentials. // - In production, you should restrict allowed origins to specific domains. // // 此中间件设置必要的头部以允许跨域请求,包括允许的来源、方法和头部。 // // 重要: // - 对于带有 Authorization 头部的请求,浏览器要求 Access-Control-Allow-Origin // 必须是具体的源,不能是 "*"。 // - 此中间件动态设置源以匹配请求的 Origin 头部,允许带凭据的跨域请求。 // - 在生产环境中,应将允许的源限制为特定域名。 // // Returns: // - gin.HandlerFunc: The CORS middleware handler. // // 返回值: // - gin.HandlerFunc: CORS 中间件处理器。 func CORS() gin.HandlerFunc { return func(c *gin.Context) { // Get the Origin header from the request // 从请求中获取 Origin 头部 origin := c.Request.Header.Get("Origin") logger.Info("CORS middleware invoked", "method", c.Request.Method, "path", c.Request.URL.Path, "origin", origin, ) // Determine the allowed origin to return. // If the origin is in our allowed list, echo it back. // Otherwise, for development, also echo it back to be permissive. // In production, you should reject unallowed origins. // // 确定要返回的允许源。如果在允许列表中,则回显。 // 开发环境下也回显其他源以方便调试。生产环境应拒绝未允许的源。 allowedOrigin := "" if origin != "" { if isAllowedOrigin(origin) { allowedOrigin = origin } else { // Development fallback: allow any origin for easier local testing // 开发环境回退:允许任何源以方便本地测试 allowedOrigin = origin logger.Warn("CORS request from unknown origin", "origin", origin, "path", c.Request.URL.Path, ) } } // Set Access-Control-Allow-Origin to the request's origin // This allows cross-origin requests with Authorization header // 将 Access-Control-Allow-Origin 设置为请求的源 // 这允许带 Authorization 头部的跨域请求 if allowedOrigin != "" { c.Writer.Header().Set("Access-Control-Allow-Origin", allowedOrigin) } // Allow credentials (cookies, Authorization headers) // 允许凭据(cookies、Authorization 头部) c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // Allow common HTTP methods // 允许常用 HTTP 方法 c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH") // Allow common headers including Authorization // 允许常用头部,包括 Authorization c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, Accept, Origin, X-Request-ID") // Expose headers to the client // 暴露头部给客户端 c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Disposition, Content-Length, X-Request-ID") // Handle OPTIONS preflight requests // 处理 OPTIONS 预检请求 if c.Request.Method == "OPTIONS" { logger.Info("CORS handling OPTIONS preflight", "origin", origin, "path", c.Request.URL.Path) c.AbortWithStatus(http.StatusOK) return } c.Next() } }