cors
在 Web 开发中,CORS(Cross-Origin Resource Sharing,跨域资源共享)是一个用于解决跨域请求的安全机制。跨域请求是指在浏览器中从一个域(如 example.com)请求另一个域(如 api.example.com)的资源。由于安全性原因,浏览器默认会限制这些跨域请求。CORS 机制允许服务器声明哪些外部域可以访问其资源,从而解决跨域问题。
1. CORS 基本原理
CORS 机制通过 HTTP 头部来控制跨域请求的权限,通常通过以下两种方式来实现:
- 简单请求(Simple Request):符合特定条件的请求,如
GET或POST请求,且没有复杂的头部信息。 - 预检请求(Preflight Request):浏览器发起一个额外的
OPTIONS请求来询问服务器,是否允许当前的跨域请求。预检请求通常出现在带有自定义请求头或使用PUT、DELETE等方法时。
2. CORS 相关 HTTP 头
2.1 Access-Control-Allow-Origin
这是 CORS 中最重要的头部,指示哪些源(域)可以访问资源。可以设置为:
*(允许所有域访问资源)- 具体的域名(如
https://example.com)
示例:
Access-Control-Allow-Origin: *或:
Access-Control-Allow-Origin: https://example.com2.2 Access-Control-Allow-Methods
指示允许的 HTTP 方法。例如,允许 GET、POST、PUT、DELETE 等方法。
示例:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE2.3 Access-Control-Allow-Headers
用于指示允许的自定义请求头。例如,如果客户端发送了一个自定义头部 X-Custom-Header,服务器需要在此头部中声明。
示例:
Access-Control-Allow-Headers: X-Custom-Header, Content-Type2.4 Access-Control-Allow-Credentials
指定是否允许发送凭证(如 cookies、HTTP 认证等)。默认情况下,CORS 请求不包括凭证,如果服务器希望允许凭证,则需要在响应头中明确设置为 true。
示例:
Access-Control-Allow-Credentials: true2.5 Access-Control-Expose-Headers
指示哪些响应头可以被浏览器访问,默认情况下,浏览器只能访问某些安全的响应头。
示例:
Access-Control-Expose-Headers: Content-Length, X-Custom-Header2.6 Access-Control-Max-Age
表示浏览器可以缓存预检请求的结果,避免每次跨域请求时都需要进行预检。单位为秒。
示例:
Access-Control-Max-Age: 86400 // 缓存预检请求 1 天3. Gin 中的 CORS 配置
在 Gin 中,设置 CORS 头部非常简单。你可以使用 gin-contrib/cors 中间件来处理 CORS。
3.1 安装 cors 包
go get github.com/gin-contrib/cors3.2 基本 CORS 配置
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 请求方法(如GET、POST等)。AllowHeaders:设置允许的请求头。AllowCredentials:如果设置为true,表示允许跨域请求携带凭证(如 cookies)。MaxAge:设置预检请求的缓存时间,这样浏览器在缓存时间内不再发送预检请求。
3.3 允许所有来源的 CORS 配置
如果你希望允许所有来源访问你的 API,可以将 AllowOrigins 设置为 *:
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 的示例:
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 请求,询问服务器是否允许跨域访问:
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/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-Custom-Header4.2 实际请求
如果预检请求得到允许,浏览器将发送实际请求:
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/1.1 200 OK
Access-Control-Allow-Origin: http://example.com
Content-Type: application/json5. 总结
- CORS 是一种机制,允许服务器声明哪些来源的请求可以访问资源。
- 在 Gin 中,使用
gin-contrib/cors中间件非常方便,可以轻松配置跨域请求的相关设置。 - 关键的 CORS 头包括
Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers等。 - 预检请求(
OPTIONS请求)用于浏览器与服务器之间协商是否允许跨域请求,具体头部用于控制权限。
通过合理的 CORS 配置,你可以确保 Web 应用能够安全地处理跨域请求,同时避免潜在的安全问题。