CORS跨域解决终极指南
0. 同源策略详解
同源策略(Same-Origin Policy) 是浏览器最核心的安全策略之一,它限制了来自不同源的文档或脚本如何与当前文档进行交互。
解决方法:后端需要实现 CORS 支持,这正是这节要详细讲解的内容。
1.CORS
摘要: 本文详细介绍了 CORS(跨域资源共享)的原理、限制及解决方案。首先解释了同源策略的概念及其对前后端分离架构的影响,然后阐述了 CORS 的工作机制,包括预检请求和响应头配置。重点讲解了如何使用 Go 语言的 Gin 框架配合 cors 中间件实现跨域访问控制,包括各种配置选项的含义和用法。最后通过完整的代码示例展示了如何在实际项目中配置 CORS 中间件和处理跨域请求。
判断是否是同一个源:协议、域名、端口。
同源访问是浏览器的核心安全策略。
当前后端分离, 前后端是不同的源,浏览器先访问前端,当前端再去访问后端时,会触发浏览器的同源访问策略,因为前后端此时不是同一个源,此时请求会发出,但响应被浏览器拦截,发现不同源后,拒绝把响应返给前端。
①浏览器会对跨域做出哪些限制?
例如:[源A]和[源B]是非同源的,则浏览器会有如下限制:
- DOM访问限制:源A的脚本不能读取和操作源B的DOM
- Cookie访问限制:源A不能访问源B的cookie
- 响应数据限制:源A可以给源B发请求,但是无法获取源B的请求(这个是主要要解决的问题,也是遇到最多的问题 )
②CORS 概述 CORS 全称: Cross - Origin Resource Sharing
(跨域资源共享),是用于控制浏览器校验跨域请求的一套规范,服务器依照 CORS 规范,添加特定响应头来控制浏览器校验,大致规则如下:
- 服务器明确表示拒绝跨域请求,或没有表示,则浏览器校验不通过
- 服务器明确表示允许跨域请求,则浏览器校验通过 备注说明:使用 CORS 解决跨域是最正统的方式,且要求服务器是"自己人",因为使用cors解决跨域是要改动服务器代码。
③解决思路:当服务器接收到探测options请求后,如果请求的源在白名单里,则在响应头里加上Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers
等关键参数,浏览器拦截响应数据,看到有这些响应头,就知道要放行,于是客户端就得到了数据,否则,客户端得不到数据。
我们可以添加一个中间件,对每个请求的执行上面的操作,第三方库cors实现了这点,我们只需要配置参数就可以了,如下
Config是配置cors中间件时的所有可选项
cors.Config{
AllowAllOrigins: false, //true标识允许所有域名
AllowOrigins: nil, //允许的域名列表
AllowOriginFunc: nil, //自定义函数来动态判断是否允许某个域名
AllowOriginWithContextFunc: nil, //同上,但是传入了gin.Context
AllowMethods: nil, //允许使用的HTTP方法,未设置时默认简单方法集合:GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS。
AllowPrivateNetwork: false, //是否允许私有网络(如局域网 IP)访问
AllowHeaders: nil, //非简单请求时,允许的请求头(如 X-Custom-Header、 Authorization)中,允许客户端实际发送的头列表。
AllowCredentials: false, //是否允许携带凭证:Cookie、HTTP认证(token)、客户端证书等
ExposeHeaders: nil, //指定哪些响应头能被前端代码读取
MaxAge: 0, //预检请求(OPTIONS)的结果可以被缓存多久
AllowWildcard: false, //是否支持通配符匹配域名(如 http://*.example.com
AllowBrowserExtensions: false,
CustomSchemas: nil,
AllowWebSockets: false,
AllowFiles: false, //是否允许 file:// 协议
OptionsResponseStatusCode: 0,
}
[!CAUTION]
哪些是需要在ExposeHeaders中指明的?哪些不需要?
默认情况下,浏览器只允许前端 JavaScript 读取以下 6 个简单响应头:
Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma
除此之外的所有响应头,如果你希望前端(例如通过 fetch 或 XMLHttpRequest)能读取到,都必须在 CORS 配置的 ExposeHeaders 中显式列出。
常见需要显式暴露的响应头举例
| 响应头 | 常见用途 |
|---|---|
Authorization |
登录后后端返回的 Bearer token 有时会放在响应头中 |
X-Total-Count |
REST API 返回数据总数(分页用) |
X-Request-Id |
用于请求追踪和调试 |
X-Pagination-Page、X-Pagination-Limit |
自定义分页信息 |
Content-Disposition |
文件下载时的文件名 |
Retry-After |
限流时告知客户端多久后重试 |
Location |
重定向或创建资源时返回新 URL |
任何自定义头(如 X-User-Id、App-Version) |
业务需要的额外信息 |
代码示例
type teacher struct {
Name string `json:"name"`
Age int `json:"age"`
Sex string `json:"sex"`
}
var teachers = []teacher{
{"小红", 12, "女"},
{"小X", 14, "男"},
{"小A", 13, "女"},
}
func verifAuth(authorization string) bool {
if authorization == "" {
return false
}
return true
}
func main() {
//导入日志
log1 := logs.Logger1
// 2. 初始化 Gin
r := gin.Default()
// 3. 设置 CORS 中间件(全局生效,白名单来自配置)
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:63342", "https://www.u2tool.com"},
AllowMethods: []string{"POST", "GET", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"},
AllowHeaders: []string{"set-cookie", "Authorization", "Content-Type"},
AllowCredentials: true,
ExposeHeaders: []string{"authorization"},
MaxAge: 1 * time.Minute,
}))
// 4. 只有登录才能访问的业务(完全不需要写任何 CORS 代码)
r.GET("/api/user", func(c *gin.Context) {
authorization := c.GetHeader("authorization")
if verifAuth(authorization) == false {
log1.Error("authorization 是空")
c.JSON(http.StatusUnauthorized, gin.H{})
return
}
log1.Info("获取到了token=", authorization)
c.JSON(http.StatusOK, gin.H{"code": 200, "data": teachers, "message": "请求成功"})
})
//登录
r.POST("/api/order", func(c *gin.Context) {
var token = "fake_abc123DEF456GHI789JKL0MNOpqrSTUvwxYZ"
c.Header("Authorization", token)
c.JSON(http.StatusOK, gin.H{"code": 200, "data": teachers, "message": "请求成功"})
})
// 5. 启动服务
r.Run(":8080")
}
2.凭证
有三种:token、cookie、TLS(少见,用于双向TSL验证)
Cookie 与 Token的核心区别
| 特性 | Cookie(自动发送) | Token(手动携带) |
|---|---|---|
| 发送方式 | 浏览器自动附加到请求的 Cookie 头中,前端无需写代码 |
前端需要手动从存储(localStorage/sessionStorage)读取并设置 Authorization: Bearer <token> |
| 跨域限制 | 默认不会跨域发送(同源策略)。要跨域需配置 withCredentials: true 且后端 AllowCredentials: true |
跨域时同样需要后端 CORS 支持 Authorization 头(在 AllowHeaders 中),但前端可手动添加该头 |
| 存储位置 | 由浏览器管理,可通过 HttpOnly 禁止 JS 读取(防XSS) |
前端代码可控(localStorage/sessionStorage),易受 XSS 攻击 |
| CSRF 风险 | 存在 CSRF 风险(恶意站点利用已保存的 Cookie 发起请求),需用 SameSite、CSRF Token 等防护 |
无(因为攻击者无法读取 token 并手动添加到 Authorization 头) |
| 移动端/非浏览器 | 不支持(因为无浏览器自动管理 Cookie) | 广泛支持,token 可明文发送 |
cookie流程:
-
用户登录成功,服务器返回:
Set-Cookie: sessionId=abc123; Path=/; HttpOnly; Secure; SameSite=Lax -
浏览器保存这个 Cookie。
-
之后用户访问同域的任何页面、API(图片、AJAX等),浏览器自动加上:
Cookie: sessionId=abc123 -
服务器收到后就知道是同一个用户
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐



所有评论(0)