当前位置: 首页 > news >正文

【计网】认识跨域,及其在go中通过注册CORS中间件解决跨域方案,go-zero、gin

一、跨域(CORS)是什么?

跨域,指的是浏览器出于安全限制,前端页面在访问不同源(协议、域名、端口任一不同)的后端接口时,会被浏览器拦截。
比如:

前端地址后端接口地址是否跨域
http://a.comhttp://a.com/api
http://a.comhttps://a.com/api是(协议不同)
http://a.comhttp://b.com/api是(域名不同)
http://a.com:8080http://a.com:8090/api是(端口不同)

浏览器会进行CORS检查。如果后端没有正确返回Access-Control-Allow-*的HTTP头信息,浏览器就会阻止前端访问。


二、浏览器的跨域流程

  1. 简单请求(Simple Request)

    • 请求方法限定:GET、POST、HEAD
    • Content-Type仅限于:application/x-www-form-urlencoded、multipart/form-data或text/plain
    • 没有对头部进行修改(如果修改,则是预检请求
  2. 预检请求(Preflight Request)

    • GET、POST、HEAD的请求方法
    • Content-Type为application/json等非简单值
    • 包含自定义头部字段

三、后端怎么处理跨域?

  • 常见跨域解决方案:CORS(跨源资源共享):W3C标准,服务端设置响应头实现

    • 处理跨域,就是要在HTTP响应中加上几个头:
  • Access-Control-Allow-Origin:允许访问的源(如*或https://example.com)

  • Access-Control-Allow-Methods:允许的HTTP方法(如GET, POST等)

  • Access-Control-Allow-Headers:允许的请求头字段

  • Access-Control-Allow-Credentials:是否允许发送Cookie(true/false)

  • Access-Control-Max-Age:预检请求缓存时间(秒)

CORS请求流程:

  1. 浏览器检测到跨域请求,自动添加Origin头
  2. 服务器检查Origin,决定是否允许并设置CORS响应头
  3. 对于预检请求,先发送OPTIONS请求确认权限
  4. 浏览器根据响应头决定是否允许实际请求

四、在 Go 后端怎么处理跨域?

这里讲两个框架:

1. 在 gin 框架中处理跨域

方法一:自己写中间件(不推荐)
func CorsMiddleware() gin.HandlerFunc {return func(c *gin.Context) {c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许所有域c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")// 处理预检请求if c.Request.Method == "OPTIONS" {c.AbortWithStatus(204)return}c.Next()}
}

然后在main.go注册:

r := gin.Default()
r.Use(CorsMiddleware())

方法二:使用第三方库(推荐)

参考go文档:https://pkg.go.dev/github.com/gin-contrib/cors

用成熟的库,比如github.com/gin-contrib/cors

go get github.com/gin-contrib/cors

代码:

import "github.com/gin-contrib/cors"// Cors 创建并返回一个gin.HandlerFunc处理程序,用于配置跨域资源共享(CORS)。
// 该函数通过调用cors.New并传递一个cors.Config结构体来实现,该结构体包含了CORS的配置信息。
// 主要目的是为了允许来自任何源的请求(AllowAllOrigins: true),并指定允许的HTTP请求头和方法,
// 同时支持凭证的跨域请求(AllowCredentials: true)。
func Cors() gin.HandlerFunc {return cors.New(cors.Config{// AllowAllOrigins: true 表示允许来自所有域的请求。AllowAllOrigins: true,// AllowHeaders 指定了允许的请求头,包括Origin, Content-Length, Content-Type和Authorization。AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization",},// AllowMethods 指定了允许的HTTP方法,包括GET, POST, PUT, DELETE, HEAD和OPTIONS。AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS",},// ExposeHeaders 指定了允许被访问的响应头,包括Content-Length, Access-Control-Allow-Origin,// Access-Control-Allow-Headers, Cache-Control, Content-Language和Content-Type。ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Cache-Control", "Content-Language", "Content-Type",},// AllowCredentials: true 表示允许跨域请求带上用户凭证。cookiesAllowCredentials: true,})
}

main.go

r := gin.Default()
r.Use(middleware.Cors(), middleware.Auth()) // 这里可以抽象到routers.go 不过要注意中间件注册一定要在路由分组之前,因为这里的注册操作相当于给所有的URL设定cors规则,前后顺序不能颠倒

r.Use()是给之后所有注册的路由挂载这个中间件。
也就是说,如果你先注册了路由,再r.Use(),那些已经注册了的路由就不会受到新的中间件的影响。
简单讲:中间件只保护在它后面注册的路由。


2. 在 go-zero 框架中处理跨域

go-zero默认就带跨域处理,只要你在服务配置里面加上跨域设置。

比如etc/your-api.yaml配置文件中,写:

Name: your-api
Host: 0.0.0.0
Port: 8888
Cors: true

注意这个Cors: true,加了以后,go-zero框架自动帮你处理CORS,包括预检请求(OPTIONS)!

如果要自定义,比如允许指定源,可以在自定义中间件里处理。

也可以自己写中间件(不推荐,go-zero已经做了)
func CorsMiddleware() func(http.Handler) http.Handler {return func(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {w.Header().Set("Access-Control-Allow-Origin", "*")w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")if r.Method == "OPTIONS" {w.WriteHeader(http.StatusNoContent)return}next.ServeHTTP(w, r)})}
}

注册:

server := rest.MustNewServer(c, rest.WithMiddlewares([]rest.Middleware{CorsMiddleware(),}...,
))

不过一般没必要,直接开Cors: true就行。


五、常见问题

  • OPTIONS请求后端返回404
    ➔ 需要后端显式处理OPTIONS方法,比如返回204 No Content。
  • 跨域时前端带了cookie但失败
    ➔ 需要Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin不能是*,必须指定具体域名。
  • 前端提示No ‘Access-Control-Allow-Origin’ header is present
    ➔ 后端没返回CORS头,检查中间件是否正确加载。

六、总结

  • 跨域是浏览器安全机制;
  • 处理跨域主要是设置HTTP响应头;
  • gin自己可以写中间件或用gin-contrib/cors
  • go-zero直接在配置里加Cors: true
  • OPTIONS预检请求一定要处理,否则请求直接失败。

七、针对go-zero中使用跨域使用方案拓展

  1. main.go方案
server := rest.MustNewServer(c.RestConf,rest.WithCustomCors(func(header http.Header) { // 1.这是自定义跨域逻辑var allowOrigin = "Access-Control-Allow-Origin"var allOrigins = "http://localhost:5173"var allowMethods = "Access-Control-Allow-Methods"var allowHeaders = "Access-Control-Allow-Headers"var exposeHeaders = "Access-Control-Expose-Headers"var methods = "GET, HEAD, POST, PATCH, PUT, DELETE, OPTIONS"var allowHeadersVal = "xxxx, Content-Type, Origin, X-CSRF-Token, Authorization, AccessToken, Token, Range"var exposeHeadersVal = "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers"var maxAgeHeader = "Access-Control-Max-Age"var maxAgeHeaderVal = "86400"header.Set(allowOrigin, allOrigins)header.Set(allowMethods, methods)header.Set(allowHeaders, allowHeadersVal)header.Set(exposeHeaders, exposeHeadersVal)header.Set(maxAgeHeader, maxAgeHeaderVal)}, func(w http.ResponseWriter) {}),)defer server.Stop()// 2.简单跨域//server := rest.MustNewServer(c.RestConf,//	rest.WithCors("http://localhost:5173"))// 3.自定义头 (预检请求)//server := rest.MustNewServer(c.RestConf,//	//顺序不能颠倒//	rest.WithCors("http://localhost:5173"),//	rest.WithCorsHeaders("xxxx"),//)

  1. 在 internal/middleware 目录下创建 cors.go 文件
package middlewareimport ("net/http"
)// Cors 中间件处理跨域请求
func Cors(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 1. 设置允许访问的源// 生产环境应替换为具体的域名,如 "https://example.com"w.Header().Set("Access-Control-Allow-Origin", "*")// 2. 设置允许的HTTP方法w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")// 3. 设置允许的请求头w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")// 4. 设置是否允许发送Cookie等凭证信息// 如果设置为true,则Access-Control-Allow-Origin不能为*w.Header().Set("Access-Control-Allow-Credentials", "false")// 5. 设置预检请求缓存时间(秒)w.Header().Set("Access-Control-Max-Age", "86400")// 6. 如果是OPTIONS方法(预检请求),直接返回204if r.Method == "OPTIONS" {w.WriteHeader(http.StatusNoContent)return}// 7. 继续处理后续中间件和路由next.ServeHTTP(w, r)})
}

在 main 函数中使用中间件

package mainimport ("flag""fmt""xxx/internal/config""xxx/internal/handler""xxx/internal/middleware" // 引入中间件包"xxx/internal/svc""github.com/zeromicro/go-zero/core/conf""github.com/zeromicro/go-zero/rest"
)var configFile = flag.String("f", "etc/xxx-api.yaml", "the config file")func main() {flag.Parse()// 1. 加载配置文件var c config.Configconf.MustLoad(*configFile, &c)// 2. 创建服务上下文ctx := svc.NewServiceContext(c)// 3. 创建服务实例server := rest.MustNewServer(c.RestConf)defer server.Stop()// 4. 注册CORS中间件(关键步骤)server.Use(middleware.Cors)// 5. 注册路由处理器handler.RegisterHandlers(server, ctx)// 6. 打印启动信息并启动服务fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)server.Start()
}

https://github.com/0voice

http://www.xdnf.cn/news/190873.html

相关文章:

  • yolov8+kalman 实现目标跟踪统计人流量
  • redis+lua+固定窗口实现分布式限流
  • 八大排序——直接插入排序/希尔排序
  • Spring Cloud初探之自定义负载均衡策略(五)
  • 让数据优雅落地:用 serde::Deserialize 玩转结构体实体
  • CasaOS上部署1Panel开源运维面板远程在线访问配置实操指南
  • K8s新手系列之K8s中的资源
  • 【杂谈】-人工智能驱动的网络安全威胁:新一代网络钓鱼
  • Azure 数字孪生是什么?
  • ​​HTTP vs HTTPS:传输协议的安全演进与核心差异​
  • 8.Android(通过Manifest配置文件传递数据(meta-data))
  • 近地卫星网络 (Low Earth Orbit Satellite Networks)入门学习笔记
  • Transformer数学推导——Q26 推导多语言Transformer中语言间注意力共享的参数效率公式
  • C语言----操作符详解(万字详解)
  • python 线程池顺序执行
  • 二叉树的所有路径(回溯算法基础)
  • 深度学习---Pytorch概览
  • 3D模型文件格式之《DAE格式介绍》
  • [LeetCode 438/567] 找到字符串中所有字母异位词/字符串的排列(滑动窗口)
  • tsconfig.json的配置项介绍
  • 云原生周刊:Kubernetes v1.33 正式发布
  • 用JavaScript构建3D程序
  • 2025系统架构师---论微服务架构及其应用
  • Linux中的系统延时任务和定时任务与时间同步服务和构建时间同步服务器
  • 老电脑优化全知道(包括软件和硬件优化)
  • 【爬虫】一文掌握 adb 的各种指令(adb备忘清单)
  • 【Mybatis】Mybatis基础
  • 集合框架篇-java集合家族汇总
  • 【3D基础】深入解析OBJ与MTL文件格式:Blender导出模型示例及3D开发应用
  • 【KWDB 创作者计划】_企业数据管理的利刃:技术剖析与应用实践