Skip to content

cors

在 Web 开发中,CORSCross-Origin Resource Sharing,跨域资源共享)是一个用于解决跨域请求的安全机制。跨域请求是指在浏览器中从一个域(如 example.com)请求另一个域(如 api.example.com)的资源。由于安全性原因,浏览器默认会限制这些跨域请求。CORS 机制允许服务器声明哪些外部域可以访问其资源,从而解决跨域问题。

1. CORS 基本原理

CORS 机制通过 HTTP 头部来控制跨域请求的权限,通常通过以下两种方式来实现:

  • 简单请求(Simple Request):符合特定条件的请求,如 GETPOST 请求,且没有复杂的头部信息。
  • 预检请求(Preflight Request):浏览器发起一个额外的 OPTIONS 请求来询问服务器,是否允许当前的跨域请求。预检请求通常出现在带有自定义请求头或使用 PUTDELETE 等方法时。

2. CORS 相关 HTTP 头

2.1 Access-Control-Allow-Origin

这是 CORS 中最重要的头部,指示哪些源(域)可以访问资源。可以设置为:

  • *(允许所有域访问资源)
  • 具体的域名(如 https://example.com

示例:

http
Access-Control-Allow-Origin: *

或:

http
Access-Control-Allow-Origin: https://example.com

2.2 Access-Control-Allow-Methods

指示允许的 HTTP 方法。例如,允许 GETPOSTPUTDELETE 等方法。

示例:

http
Access-Control-Allow-Methods: GET, POST, PUT, DELETE

2.3 Access-Control-Allow-Headers

用于指示允许的自定义请求头。例如,如果客户端发送了一个自定义头部 X-Custom-Header,服务器需要在此头部中声明。

示例:

http
Access-Control-Allow-Headers: X-Custom-Header, Content-Type

2.4 Access-Control-Allow-Credentials

指定是否允许发送凭证(如 cookies、HTTP 认证等)。默认情况下,CORS 请求不包括凭证,如果服务器希望允许凭证,则需要在响应头中明确设置为 true

示例:

http
Access-Control-Allow-Credentials: true

2.5 Access-Control-Expose-Headers

指示哪些响应头可以被浏览器访问,默认情况下,浏览器只能访问某些安全的响应头。

示例:

http
Access-Control-Expose-Headers: Content-Length, X-Custom-Header

2.6 Access-Control-Max-Age

表示浏览器可以缓存预检请求的结果,避免每次跨域请求时都需要进行预检。单位为秒。

示例:

http
Access-Control-Max-Age: 86400  // 缓存预检请求 1 天

3. Gin 中的 CORS 配置

在 Gin 中,设置 CORS 头部非常简单。你可以使用 gin-contrib/cors 中间件来处理 CORS。

3.1 安装 cors 包

bash
go get github.com/gin-contrib/cors

3.2 基本 CORS 配置

go
package main

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-contrib/cors"
	"time"
)

func main() {
	r := gin.Default()

	// 配置 CORS
	r.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"http://example.com", "http://anotherdomain.com"}, // 允许的来源
		AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},                    // 允许的请求方法
		AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},         // 允许的请求头
		AllowCredentials: true, // 是否允许携带凭证(cookies)
		MaxAge:           12 * time.Hour, // 预检请求缓存时间
	}))

	// 定义路由
	r.GET("/", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello, world!",
		})
	})

	// 启动服务
	r.Run(":8080")
}

在这个示例中:

  • AllowOrigins:设置允许哪些来源的请求访问资源,可以是一个域名列表。如果允许所有域名,可以使用 *
  • AllowMethods:设置允许的 HTTP 请求方法(如 GETPOST 等)。
  • AllowHeaders:设置允许的请求头。
  • AllowCredentials:如果设置为 true,表示允许跨域请求携带凭证(如 cookies)。
  • MaxAge:设置预检请求的缓存时间,这样浏览器在缓存时间内不再发送预检请求。

3.3 允许所有来源的 CORS 配置

如果你希望允许所有来源访问你的 API,可以将 AllowOrigins 设置为 *

go
r.Use(cors.New(cors.Config{
	AllowOrigins:     []string{"*"}, // 允许所有域访问
	AllowMethods:     []string{"GET", "POST", "PUT", "DELETE"},
	AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
	AllowCredentials: true,
	MaxAge:           12 * time.Hour,
}))

3.4 针对单个路由配置 CORS

你还可以选择只在某些路由上启用 CORS,而不是全局启用。以下是为特定路由添加 CORS 的示例:

go
r.GET("/public", cors.New(cors.Config{
	AllowOrigins: []string{"http://example.com"},
}), func(c *gin.Context) {
	c.JSON(200, gin.H{
		"message": "This route has CORS enabled for example.com",
	})
})

4. CORS 和 HTTP 请求

当浏览器发起跨域请求时,首先会发起一个预检请求OPTIONS 请求)来询问服务器是否允许该请求。如果服务器正确配置了 CORS,它会返回相应的 CORS 头,允许浏览器继续发送实际的请求。

4.1 预检请求(OPTIONS)示例

浏览器发送一个 OPTIONS 请求,询问服务器是否允许跨域访问:

http
OPTIONS /resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Custom-Header

服务器响应 CORS 头:

http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header

4.2 实际请求

如果预检请求得到允许,浏览器将发送实际请求:

http
POST /resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
Content-Type: application/json
Authorization: Bearer token
X-Custom-Header: some-value

服务器响应实际请求:

http
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Content-Type: application/json

5. 总结

  • CORS 是一种机制,允许服务器声明哪些来源的请求可以访问资源。
  • 在 Gin 中,使用 gin-contrib/cors 中间件非常方便,可以轻松配置跨域请求的相关设置。
  • 关键的 CORS 头包括 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 等。
  • 预检请求(OPTIONS 请求)用于浏览器与服务器之间协商是否允许跨域请求,具体头部用于控制权限。

通过合理的 CORS 配置,你可以确保 Web 应用能够安全地处理跨域请求,同时避免潜在的安全问题。