Gin框架---框架CORS

一、什么是跨域?为什么要跨域?

1. 同源策略

浏览器的同源策略(Same-Origin Policy)是一个重要的安全机制,它限制了一个源(origin)的文档或脚本如何与另一个源的资源进行交互。

源(origin) 由三部分组成:

  • 协议(http / https)
  • 域名(example.com
  • 端口(80 / 443 / 8080)

如果两个 URL 的协议、域名、端口都相同,则它们同源 ,否则就是跨域

例如:

  • http://localhost:8080/apihttp://localhost:8080/user 同源(协议、域名、端口相同)
  • http://localhost:8080/apihttp://localhost:3000/api 跨域(端口不同)
  • http://example.com/apihttps://example.com/api 跨域(协议不同)

同源策略限制了以下行为:

  • Cookie、LocalStorage 等无法跨域读取
  • DOM 无法跨域访问
  • AJAX 请求无法跨域发送(会被浏览器拦截)

2. 跨域需求

在实际开发中,前后端分离很常见:前端运行在 http://localhost:3000,后端运行在 http://localhost:8080。这时前端发起的 AJAX 请求就属于跨域请求,会被浏览器拦截。为了让前后端能够正常通信,就需要跨域资源共享(CORS)。


二、CORS 是什么?

CORS(Cross-Origin Resource Sharing,跨域资源共享) 是一种机制,它使用额外的 HTTP 头来告诉浏览器,允许某个源的网页访问另一个源的资源。

1. 基本流程

浏览器在发起跨域请求时,会携带 Origin 头,服务器在响应中携带 Access-Control-Allow-Origin 等头,浏览器根据这些头决定是否允许跨域访问。

2. 简单请求与非简单请求

简单请求

满足以下所有条件:

  • 方法为 GETHEADPOST 之一
  • 头部只包含 AcceptAccept-LanguageContent-LanguageContent-Type(且值为 application/x-www-form-urlencodedmultipart/form-datatext/plain
  • 没有自定义头部

简单请求浏览器直接发送请求,响应中若 Access-Control-Allow-Origin 匹配当前源,则允许访问。

非简单请求(预检请求)

不符合简单请求条件的请求(如使用 PUTDELETE、自定义头、Content-Type: application/json 等),浏览器会先发送一个 OPTIONS 请求(预检请求),询问服务器是否允许实际请求。服务器需响应正确的 CORS 头,浏览器再根据结果决定是否发送实际请求。


三、Gin 框架中如何配置 CORS

在 Gin 中,通常使用中间件来处理 CORS。最常用的是官方维护的 github.com/gin-contrib/cors 包。

1. 安装

bash 复制代码
go get github.com/gin-contrib/cors

2. 基本用法

go 复制代码
package main

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

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

    // 使用默认 CORS 中间件
    r.Use(cors.Default())

    r.GET("/api/data", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello CORS"})
    })

    r.Run(":8080")
}

cors.Default() 的默认配置:

  • 允许所有源 *
  • 允许方法:GETPOSTPUTPATCHDELETEHEADOPTIONS
  • 允许头:OriginContent-LengthContent-Type
  • 不允许携带凭证(Credentials)

3. 自定义配置

更常见的做法是根据项目需求自定义 CORS 配置。

go 复制代码
package main

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

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

    // 自定义 CORS 配置
    r.Use(cors.New(cors.Config{
        // 允许的源,可以设置多个,或者使用 "*" 允许所有
        AllowOrigins:     []string{"http://localhost:3000", "https://example.com"},
        // 允许的方法
        AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        // 允许的头
        AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
        // 暴露的头(前端可读取)
        ExposeHeaders:    []string{"Content-Length"},
        // 是否允许携带凭证(Cookie、HTTP认证等)
        AllowCredentials: true,
        // 预检请求缓存时间
        MaxAge:           12 * time.Hour,
    }))

    r.GET("/api/data", func(c *gin.Context) {
        c.JSON(200, gin.H{"message": "Hello CORS"})
    })

    r.Run(":8080")
}

4. 使用通配符

如果需要允许所有源,可以将 AllowOrigins 设为 []string{"*"},但此时 AllowCredentials 必须为 false,因为浏览器不允许带凭证的通配符跨域。

如果既要允许所有源又要支持凭证,可以使用 AllowOriginFunc 动态判断:

go 复制代码
r.Use(cors.New(cors.Config{
    AllowOriginFunc: func(origin string) bool {
        // 可以在这里写自定义逻辑,比如检查 origin 是否在白名单中
        return true // 允许所有
    },
    AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
    AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
    AllowCredentials: true,
}))

5. 预检请求处理

Gin 的 CORS 中间件会自动处理 OPTIONS 请求,无需手动编写。但如果你自己写中间件,需要确保对 OPTIONS 方法返回正确的响应头并停止后续处理。


四、手动实现 CORS 中间件(可选)

为了更深入理解,可以自己写一个简单的 CORS 中间件:

go 复制代码
func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204) // 预检请求直接返回
            return
        }

        c.Next()
    }
}

然后在路由中调用:

go 复制代码
r.Use(CORSMiddleware())

五、注意事项

  1. 预检请求 OPTIONS
    如果前端发起的是非简单请求,会先发 OPTIONS。确保你的路由或中间件能正确处理 OPTIONS 方法,否则浏览器会报错。
  2. AllowCredentials 与通配符
    AllowCredentials: true 时,AllowOrigins 不能为 *,必须指定具体源,或者使用 AllowOriginFunc 动态返回允许。
  3. 安全考虑
    生产环境中不要滥用 * 允许所有源,应只配置实际需要的前端域名,避免 CSRF 等风险。
  4. 多个中间件顺序
    CORS 中间件应放在最前面,确保在所有其他路由处理之前就设置好响应头,特别是 OPTIONS 请求能被正确拦截。
  5. 自定义头
    如果你的前端在请求中添加了自定义头(如 X-Token),需要在 AllowHeaders 中明确列出,否则浏览器会认为请求不合法。
  6. 缓存预检结果
    通过设置 MaxAge 可以减少不必要的 OPTIONS 请求,提升性能。

六、完整示例(带日志)

go 复制代码
package main

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

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

    // 配置 CORS
    r.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"http://localhost:3000"},
        AllowMethods:     []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
        AllowHeaders:     []string{"Origin", "Content-Type", "Authorization"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        MaxAge:           12 * time.Hour,
    }))

    // 测试路由
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

前端访问 http://localhost:3000 下的页面,通过 AJAX 请求 http://localhost:8080/ping,即可成功跨域通信。


总结

  • 同源策略 是浏览器安全基础,CORS 是突破同源限制的标准方案。
  • CORS 通过 HTTP 头实现,浏览器和服务器协商是否允许跨域。
  • Gin 框架中通常使用 gin-contrib/cors 中间件 快速配置,也可手动实现。
  • 配置时注意预检请求凭证与通配符的限制 ,以及生产环境的安全性
相关推荐
不会写DN16 小时前
GORM 实战入门:从环境搭建到企业级常用特性全解析
sql·mysql·go·gin
Vic1010118 小时前
Wireshark 解密 HTTPS 流量
测试工具·https·wireshark
2501_9160088920 小时前
2026 iOS 证书管理,告别钥匙串依赖,构建可复制的签名环境
android·ios·小程序·https·uni-app·iphone·webview
zl_dfq21 小时前
计算机网络 之 【http协议】(简易HTTP服务器实现逻辑)
服务器·计算机网络·http
serve the people1 天前
ACME 协议流程与AllinSSL 的关系(二)
网络协议·https·ssl
serve the people1 天前
ACME 协议流程与AllinSSL 的关系(三)
服务器·网络·https
漠月瑾-西安1 天前
Cookie Secure 属性:守护网络传输安全的关键防线
网络安全·https·web开发·安全配置·cookie安全·会话保护
zl_dfq1 天前
计算机网络 之 【http协议】(http的无状态性、Cookie与Session的简介)
网络协议·计算机网络·http
添砖java‘’1 天前
应用层协议HTTP
网络·网络协议·http