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.ContextAllowMethods: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.comAllowBrowserExtensions: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) | 业务需要的额外信息 |
代码示例
typeteacherstruct{Namestring`json:"name"`Ageint`json:"age"`Sexstring`json:"sex"`}varteachers=[]teacher{{"小红",12,"女"},{"小X",14,"男"},{"小A",13,"女"},}funcverifAuth(authorizationstring)bool{ifauthorization==""{returnfalse}returntrue}funcmain(){//导入日志log1:=logs.Logger1// 2. 初始化 Ginr:=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")ifverifAuth(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){vartoken="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服务器收到后就知道是同一个用户
