跳到主要内容

CORS 中间件

CORS (Cross-Origin Resource Sharing) 中间件用于处理跨域资源共享,允许您控制哪些域可以访问您的 API 资源。

使用方法

基础用法

import (
"go-slim.dev/slim"
"go-slim.dev/slim/middleware"
)

func main() {
s := slim.New()

// 使用默认配置
s.Use(middleware.CORS())

s.GET("/api/users", func(c slim.Context) error {
return c.JSON(200, map[string]string{"message": "Hello"})
})

s.Start(":8080")
}

自定义配置

s := slim.New()

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"https://example.com", "https://app.example.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Content-Type", "Authorization"},
AllowCredentials: true,
ExposeHeaders: []string{"X-Request-ID"},
MaxAge: 3600,
}))

配置选项

CORSConfig

type CORSConfig struct {
// AllowOrigins 定义允许访问资源的源列表
// 可选。默认值 []string{"*"}
AllowOrigins []string

// AllowOriginFunc 是一个自定义函数,用于验证源
// 如果设置此选项,AllowOrigins 将被忽略
// 可选
AllowOriginFunc func(origin string) (bool, error)

// AllowMethods 定义访问资源时允许的 HTTP 方法列表
// 用于响应预检请求
// 可选。默认值包含常用方法
AllowMethods []string

// AllowHeaders 定义实际请求中可以使用的请求头列表
// 用于响应预检请求
// 可选。默认值 []string{}
AllowHeaders []string

// AllowCredentials 指示当凭证标志为 true 时,
// 是否可以暴露对请求的响应
// 可选。默认值 false
AllowCredentials bool

// ExposeHeaders 定义客户端允许访问的响应头白名单
// 可选。默认值 []string{}
ExposeHeaders []string

// MaxAge 指示预检请求的结果可以被缓存多长时间(秒)
// 可选。默认值 0
MaxAge int
}

使用示例

1. 允许所有源(开发环境)

s.Use(middleware.CORS())

默认配置允许所有源访问(AllowOrigins: []string{"*"}),适合开发环境。

2. 限制特定域名

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{
"https://example.com",
"https://app.example.com",
},
}))

3. 使用通配符模式

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{
"https://*.example.com", // 允许所有 example.com 的子域名
"http://localhost:*", // 允许 localhost 的所有端口
},
}))

4. 自定义源验证函数

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOriginFunc: func(origin string) (bool, error) {
// 自定义逻辑,例如从数据库查询
allowedOrigins := []string{
"https://example.com",
"https://app.example.com",
}

for _, allowed := range allowedOrigins {
if origin == allowed {
return true, nil
}
}
return false, nil
},
}))

5. 支持凭证(Cookie)

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"https://example.com"},
AllowCredentials: true,
AllowHeaders: []string{"Content-Type", "Authorization"},
}))

注意:当 AllowCredentials: true 时,AllowOrigins 不能设置为 ["*"],必须指定具体的域名。

6. 完整配置示例

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{
"https://example.com",
"https://app.example.com",
},
AllowMethods: []string{
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"OPTIONS",
},
AllowHeaders: []string{
"Origin",
"Content-Type",
"Accept",
"Authorization",
"X-Request-ID",
},
ExposeHeaders: []string{
"X-Request-ID",
"X-Response-Time",
},
AllowCredentials: true,
MaxAge: 3600, // 1 小时
}))

工作原理

简单请求

对于简单请求(Simple Request),CORS 中间件会:

  1. 检查请求的 Origin
  2. 验证源是否在允许列表中
  3. 如果允许,设置 Access-Control-Allow-Origin 响应头
  4. 如果配置了 AllowCredentials,设置相应的响应头
  5. 继续处理请求

预检请求

对于预检请求(Preflight Request,HTTP OPTIONS),CORS 中间件会:

  1. 检查请求的 Origin
  2. 验证源是否在允许列表中
  3. 设置 Access-Control-Allow-Origin
  4. 设置 Access-Control-Allow-Methods
  5. 设置 Access-Control-Allow-Headers
  6. 如果配置了 MaxAge,设置 Access-Control-Max-Age
  7. 返回 204 No Content 状态码

默认配置

CORSConfig{
AllowOrigins: []string{"*"},
AllowMethods: []string{
"GET",
"HEAD",
"PUT",
"PATCH",
"POST",
"DELETE",
},
AllowHeaders: []string{},
AllowCredentials: false,
ExposeHeaders: []string{},
MaxAge: 0,
}

最佳实践

1. 生产环境限制源

// 开发环境
if os.Getenv("ENV") == "development" {
s.Use(middleware.CORS())
} else {
// 生产环境
s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{
"https://yourdomain.com",
"https://app.yourdomain.com",
},
AllowCredentials: true,
}))
}

2. 使用环境变量配置

allowedOrigins := strings.Split(os.Getenv("ALLOWED_ORIGINS"), ",")

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: allowedOrigins,
}))

3. 限制暴露的响应头

只暴露客户端真正需要的响应头:

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
ExposeHeaders: []string{
"X-Request-ID",
"X-Total-Count", // 分页时可能需要
},
}))

4. 合理设置 MaxAge

设置合理的缓存时间可以减少预检请求的数量:

s.Use(middleware.CORSWithConfig(middleware.CORSConfig{
MaxAge: 86400, // 24 小时
}))

安全考虑

  1. 不要在生产环境使用 AllowOrigins: ["*"],这会允许任何网站访问您的 API

  2. 谨慎使用 AllowCredentials: true,确保您了解其安全影响

  3. 限制 AllowMethods,只允许必要的 HTTP 方法

  4. 限制 AllowHeaders,只允许必要的请求头

  5. 使用 HTTPS,避免在不安全的连接上传输敏感信息

常见问题

Q: 为什么 CORS 请求失败?

A: 检查以下几点:

  • 服务器是否正确配置了 CORS 中间件
  • AllowOrigins 是否包含客户端的域名
  • 如果使用凭证(Cookie),是否设置了 AllowCredentials: true
  • 是否正确设置了 AllowHeadersAllowMethods

Q: 什么是预检请求?

A: 预检请求是浏览器在发送某些跨域请求前自动发送的 OPTIONS 请求,用于检查服务器是否允许实际请求。

Q: AllowOrigins: ["*"]AllowCredentials: true 能同时使用吗?

A: 不能。出于安全考虑,当启用凭证时,必须指定具体的源,不能使用通配符 *

参考资料