gin-core跨域问题
今天使用one-api时, 突然浏览器报跨域错误, 我就开始找bug.
这个问题也是面试中问到比较多的问题, 所以我也总结一下, 滤清自己的思路
为什么浏览器要有同源策略? (这个策略就是产生跨域问题的原因)
咱们假设一个场景:
你自己做的网站, 里面有一个请求是请求b站的个人用户名, 我通过某个操作, 点击了什么按钮, 会导致我开始请求我的b站个人用户名, 同时回来的消息也会别你做的网站中的js看到, 这样就会导致隐私泄漏, 这是不对的, 所以浏览器有了这个行为.
怎么解决这个问题
-
如果你可以操作b站服务器的话, 你在服务器处理后, 在响应头里面加一条信息:Access-Control-Allow-Origin: *
. 这样别人就可以随意请求了. 如果你还想细粒度控制, 还有挺多别的可以选择
-
变成同源的. 因为浏览器的要求, 就是你不能看别人网站的信息, 那么你就可以通过证明你看的确实是你的东西. 这样你可以通过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文件了.