gin-core的跨域问题分析与解决

详细分析了gin-core框架中的跨域问题,从浏览器同源策略的原理出发,结合实际案例讲解了如何通过正确配置nginx来解决跨域问题。

gin-core跨域问题

今天使用one-api时, 突然浏览器报跨域错误, 我就开始找bug.

这个问题也是面试中问到比较多的问题, 所以我也总结一下, 滤清自己的思路

为什么浏览器要有同源策略? (这个策略就是产生跨域问题的原因)

咱们假设一个场景:

你自己做的网站, 里面有一个请求是请求b站的个人用户名, 我通过某个操作, 点击了什么按钮, 会导致我开始请求我的b站个人用户名, 同时回来的消息也会别你做的网站中的js看到, 这样就会导致隐私泄漏, 这是不对的, 所以浏览器有了这个行为.

怎么解决这个问题

  1. 如果你可以操作b站服务器的话, 你在服务器处理后, 在响应头里面加一条信息:Access-Control-Allow-Origin: *. 这样别人就可以随意请求了. 如果你还想细粒度控制, 还有挺多别的可以选择123

  2. 变成同源的. 因为浏览器的要求, 就是你不能看别人网站的信息, 那么你就可以通过证明你看的确实是你的东西. 这样你可以通过nginx反向代理来解决. 通过不同的资源请求地址来让nginx判断是请求哪个地址.

    • 比如前端统一是/
    • 后端统一是/api
    • 这样就可以区分开了

说说我遇到的bug

我的环境是: 使用TLS的3000端口作为one-api的接口, 使用TLS的443端口作为前端页面接口, 并且我用的是开源项目, 所以我不是很清楚他的响应头是什么时候加上的, 我就开始看原代码:核心代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
func (cors *cors) applyCors(c *gin.Context) {
	origin := c.Request.Header.Get("Origin")
	if len(origin) == 0 {
		// request is not a CORS request
		return
	}
	host := c.Request.Host

	if origin == "http://"+host || origin == "https://"+host {
		// request is not a CORS request but have origin header.
		// for example, use fetch api
		return
	}

	if !cors.isOriginValid(c, origin) {
		c.AbortWithStatus(http.StatusForbidden)
		return
	}

	if c.Request.Method == "OPTIONS" {
		cors.handlePreflight(c)
		defer c.AbortWithStatus(cors.optionsResponseStatusCode)
	} else {
		cors.handleNormal(c)
	}

	if !cors.allowAllOrigins {
		c.Header("Access-Control-Allow-Origin", origin)
	}
}

这是gin-core的库, 其中写了如果请求头origin字段和请求头host字段相等, 那么就不添加响应头Access-Control-Allow-Origin: *了.

恰好, 我的运行环境是nginx代理到one-api的, 并且我的nginx配置文件是这样的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
server{
    listen 3000 ssl;
   location / {
        add_header X-Host-Value $host;
        client_max_body_size  64m;
        proxy_http_version 1.1;
        proxy_pass http://oneapi-one-api-1:3000;  # 请根据实际情况修改你的端口
        # proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header Accept-Encoding gzip;
        proxy_read_timeout 300s;  # GPT-4 需要较长的超时时间,请自行调整
   }
}

重点看我注释的那条的行为, 这是要把host改成$host, 而我的$host是xxx.com根本不是原本的xxx.con:3000, 这就导致了gin-core会误以为这是同源的, 就不加Access-Control-Allow-Origin: *了. 所以有个那个bug. 大功告成, 我要去提一个pr让作者看我的这篇文章后, 修改一下示例的nginx文件了.

使用 Hugo 构建
主题 StackJimmy 设计