大家都是草台班子😂,我干了这么多年开发,能把跨域问题说清楚的人也没几个😅

2024 年 7 月 11 日
 shadowyue
跨域只会出现在浏览器环境中!
跨域只会出现在浏览器环境中!
跨域只会出现在浏览器环境中!
重要的事情说三遍。
我知道为啥很多后端开发很疑惑为啥有这种问题,因为纯服务端之间应该没有这个概念。

首先解释清楚什么情况下,会被认定成跨域:
页面地址的域名是 A ,但是接口请求的地址是 B 。这就是跨域,跨越了不同的域(名)想要去请求资源。

在浏览器中会发生的现象:
浏览器会阻止给和页面地址不同的域名发请求。
这跟语言无关,这就是一个在浏览器环境下的安全策略。

怎么办:
请把你的页面域名加入白名单。使用响应头 Access-Control-Allow-Origin 来处理。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
或者使用 nginx ,前端请求的接口还是用网页的域名,但是用 ngxin 转发到服务端的地址去。

拓展:
其实后端的响应头是能帮助前端做不少事情的。
比如 Access-Control-Allow-Methods 这个头,能约定接口只能发 post,还是 get
比如 Access-Control-Allow-Headers,后端通常会返回很多响应头字段,
但是在浏览器环境下,为了安全,浏览器只允许 js 访问固定的几个响应头。
如果想让前端访问其它的响应头字段,就可以通过 Allow-Headers 进行配置。

比如如果你想让前端做一个下载进度条,正确的返回 Content-Length ,前端就能计算下载进度。

比如 Content-Disposition ,只要你声明本次请求是一个附件并正确的有文件名称,
就能自动触发浏览器的文件下载,不需要前端在额外做任何事情。

疑问 1:为啥开发环境都没事啊?
类似这个帖子的疑问: https://v2ex.ih06.com/t/1056317
开发的时候前端本地页面地址是 localhost ,接口地址肯定是其他的,为啥不跨域?
因为现在前端项目,开发用的脚手架通常会在本地用 node.js 启动一个 http 服务,
本来发给 B 域名的请求,会被代码改写成请求到 localhost(或者 127.0.0.1)的 http 服务去,
然后通过 node.js 的转发进行真正的接口调用。
正如我上面说的,跨域只会存在于浏览器环境,node.js 可以给任何域名发 http 请求。

我认为这是现在的前端脚手架提供的一个极其糟糕的功能。
它把跨域这个完全由后端处理的问题默默的在开发环境处理掉了,并且还附加了接口地址改写的各种功能
导致前后端都稀里糊涂的。跨域问题就应该在开发环境处理掉。

疑问 2:
既然开发环境前端都可以自行处理跨域,那我打包部署的时候部署一样应该可以啊?
类似这个帖子的疑问: https://v2ex.ih06.com/t/1056317
因为普通的前端构建打包后,只有前端的代码。不会包含任何 node.js 的代码。
现在的前端项目就是真真正正的一堆静态资源。不像以前的 jsp 需要服务器跑。
只需要一个 ngxin 来提供静态文件访问的能力就行。

疑问 3:不对啊,我在自己网站可以随便链接好多别的域名的涩图,不也跨域了?
对图片这种资源限制没那么严格,其它类型资源也有这个问题。
现在是可以通过 CSP 策略来告诉浏览器,我只能从什么域名加载什么样的资源。
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

疑问 4:为啥我见过浏览器发 option 请求?
正如上面说的,这是浏览器的安全策略。但是有一个问题,如果浏览器从来就没请求过 B 域名,
它怎么知道页面当前域名在不在 B 域名的白名单中呢?
所以就有 option 请求,一个不带任何数据的请求,就是问一下 B 域名的服务器,你给我了啥权限。


个人结尾来点感想:
我不是嘲讽说谁谁菜不懂这个技术点,我干了这么久开发,深知每个人都有自己的局限性。
比如我上面的解释就是我当前的理解,如果有错误那就是我的局限性。
看了我上面解释的一大堆,猜到我本职是后端开发还是前端开发了吗?
如果我上面有任何错误欢迎指正批评。
我知道 V2EX 这里经常嘲讽前端是娱乐圈,但是前端开发作为客户端开发的一个分支,
在加上由于 electron 这个牛逼项目的普及,大家使用的客户端软件,事实上很多都已经是用前端页面来做了。
比如 vscode 就是。包括各种小程序等等,本质都是 web 端的扩展。
只要有技术力还是能做出很棒的软件的。(我之前听说马斯克的很多项目 UI 层都是 electron ,上太空都行)
50866 次点击
所在节点    程序员
272 条回复
Xrall
2024 年 7 月 11 日
后面的都能理解,第一点的确是不知道的。
之前就有时候会出现前端请求跨域,然后就会让后端处理一下跨域。
但是奇怪的就是游览器报跨域后端压根没有收到任何请求。
那么 OP 说的第一点就让我稍微想得通一点了,肯定是出现了 node 的服务出现了异常导致没法正确转发。
回想一下也是每次遇见这种情况往往是本地重启一下服务就好了。
想想以前全都是 ajax 一把嗦,后端配置好了还真没遇见过前面说到的这种问题。
lisongeee
2024 年 7 月 11 日
你这有点东西都没说清楚,不严谨,比如你说的《为啥我见过浏览器发 option 请求?》

这叫预检请求,不是每次都发,只有当发起复杂请求时才会发起,使用 fetch 、xhr 发起 无复杂参数(浏览器认为无副作用)的 get 和 post 是 *没有预检请求* 的,而且服务器能收到并且正确处理

但是也需要返回允许 cors 的 headers ,否则浏览器不会把 response 传递给 js
shadowyue
2024 年 7 月 11 日
@Xrall 浏览器的做法是跨域了直接把请求拦截了,后端啥日志都不会有的。
byte10
2024 年 7 月 11 日
(⊙o⊙)…有一个东西 我觉得应该可以讲一下,为啥会有跨域问题。。。不限制 跨域会有什么问题,小程序为啥没有跨域问题。
nthin0
2024 年 7 月 11 日
感谢讲解,学习了
ylh1024
2024 年 7 月 11 日
chrome --user-data-dir="C:/Chrome dev session" --disable-web-security --disable-site-isolation-trials
silencil
2024 年 7 月 11 日
很好!我个人觉得我是理解跨域的,也知道是浏览器的策略。但是一旦被问的话,估计也是说不上来。另外,我就是这种三四年的菜鸡,也不会配置 Nginx 。
Xrall
2024 年 7 月 11 日
@shadowyue #43 这个是知道的游览器做了拦截。
只不过还是有一点点疑惑,那就是我描述的这种情况的话。
原因是游览器发送了真正的请求地址从而被游览器认定跨域拦截,
还是说像描述中一样是因为一些问题导致 node 启动的服务没有正确转发呢?
yuezhiyuan
2024 年 7 月 11 日
但是在浏览器环境下,为了安全,浏览器只允许 js 访问固定的几个响应头。
----
如何理解这句话,例如接口返回了不允许的响应头,浏览器报跨域错误吗
GloryIsMine
2024 年 7 月 11 日
这种问题在配置对象存储服务的时候也会遇到吧 不算小众问题吧
yuezhiyuan
2024 年 7 月 11 日
跨域比想象中要复杂许多,例如 Access-Control-Allow-Origin: * 等于*时另外个跨域 cookie 的参数就不能等于 true

另外受 cdn 、nginx 的影响,响应头会被不小心更改到,导致产生跨域
zliea
2024 年 7 月 11 日
不过一个项目开发的时候,如果项目组前后台沟通流畅的话,提前规划接口与前台页面的路径。
比如
{BASEURL}/yewuA/h5/是前台部署路径,{BASEURL}/yewuA/api/是后台部署路径。
前端可以控制 baseurl 放在不同环境的环境变量中打包。
前端脚手架来解决跨域问题是可以的,总不能现在还是让前端启动配置一个和测试生产环境一样的 nginx 来调试吧。
现在前端由于是编译的,没法做到使用类似../../这种相对路径请求接口,但前端接口请求一定不能是完整地址待域名端口(类似 http://域名:端口/)这种,一定要是直接到根这种。
azhangbing
2024 年 7 月 11 日
因为跨域限制是古早时候 浏览器的同源策略 那时候一般都放在同个域名,端口下的 很早很早了
yusf
2024 年 7 月 11 日
跨域是浏览器行为
xiaochena
2024 年 7 月 11 日
@bluicezhen Access-Control-Allow-Origin: * 就不能带 cookie 了哦
0IuL7w7X5K2HJxZf
2024 年 7 月 11 日
跨域的问题不只是原理,你以为理解了,MDN 那个文档看过无数遍了,但是每次和前端对接都会出现新的问题,xhr ,fetch ,axios ,不同的库对 cors 的使用方法和处理方式不同,烦人的很,而且现在的跨域限制的更多了,以前能行的办法突然发现某些情况下又不行了。
iOCZS
2024 年 7 月 11 日
人们制定了很多规则,但是又不善于记忆规则。
encro
2024 年 7 月 11 日
讲点核心的:


首先,你得理解为什么浏览器要限制跨域:
1 ,比如我公司的静态 js 和图片,被某大流量网站直接引用了,我流量不直接被刷爆?
2 ,某域名直接 iframe 之类嵌套,或者引用我的资料,打着李逵额名号,其实是李鬼。

所以,浏览器限制了跨域请求。

问:那么,我们开发时,想要访问线上接口怎么办?

答:我们可以用 vite 之类 nodejs 的代理功能,通过代理访问线上接口,代理一词的意思是我们访问本地的接口,代理服务器通过本地程序(非浏览器)转发到线上接口。

问:如果我们确实有线上项目的跨域请求的需求怎么办?

答:我们可以在服务器设置 Access-Control-Allow-Origin ,将我们自己人加入白名单。


跨域,就是这么简单!
sofm
2024 年 7 月 11 日
@Xrall 跨域时, 后端有收到请求,也响应了,前端有收到了响应,但是浏览器发现了跨域,存在安全问题,就报错拦截了。
bertonzh
2024 年 7 月 11 日
「我认为这是现在的前端脚手架提供的一个极其糟糕的功能」
你这个观点不对。

本地开发服务器会提供接口代理,它的一个重要的背景是:
世界上绝大多数网页服务,网页域名跟接口域名是一样的,也就是线上一般不会存在跨域问题。

开发工具做这个代理逻辑,仅仅是为了跟线上保持一致而已。

确实存在接口域名跟网页域名不一样的时候,但是这不应该是常态。因为即使处理了跨域的问题,还有三方 cookie 的问题等着你。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://v2ex.ih06.com/t/1056504

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX