crossorigin=\”anonymous\”导致的跨域问题

背景

产品反馈好几个同事先访问https://sh.yy.com之后切换到https://cd.yy.com浏览我们的刚上线的网页的时出现图片加载不出来的情况。经过排查发现是js没加载成功导致懒加载没生效,外在表现就是图片没加载成功。控制台报错如下:

控制台错误模拟

交代下https://j1.cdn.com.cn/aa.js是放在cdn上的静态资源(资源每次上线会更新版本号,不会缓存),上线的网站域名如下https://cd.yy.comhttps://sh.yy.com (三级域名是代表城市,前面cd代表成都,bj代表北京);

现象

当前域名为https://cd.yy.com,打开Network;

查看https://j1.cdn.com.cn/aa.js请求返回值,,response headersaccess-control-allow-origin:https://sh.yy.com。也就像控制台的报错一样,与当前域名不一致导致跨域。但是我们看请求的另外一个文件https://j1.cdn.com.cn/bb.js,这个的access-control-allow-origin:https://cd.yy.com,及是当前域名,bb.js文件请求就没有任何问题。

疑问

1.为什么我这边几个开发的电脑都没有这种问题,但是产品却大概率有这问题,有时候刷新就恢复正常了?
2.为什么https://j1.cdn.com.cn/aa.jsresponse headers中的域名不是当前域名?
3.为什么同样是j1.cdn.com.cn域名下两个文件(aa.js和bb.js)的access-control-allow-origin却不一样?

猜测

通过bug出现反馈的操作流程和报错信息来看,应该是cdn资源被缓存住了。由于开发这边每次看问题常年喜欢打开控制台而且勾上disable cache,所以每次请求都没有走浏览器的缓存。将disable cache取消并且进行切换域名操作果然复现了。那么其他疑问产生了。

疑问4:为什么浏览器的disable cache项可以影响到后端返回的response headers,以至于资源无法加载?

疑问5:还是说这个q请求所谓的response headers是由于资源请求有问题,浏览器自身机制直接取上次的值呢?

通过在iTerm执行命令行 curl -IL https://j1.cdn.com.cn/aa.js,可以看到response headers中的access-control-allow-origin确实是https://sh.yy.com,说明疑问5中的说法不成立,response headers确实是服务器端返回的。

返回值模拟

那么我们看一下这个资源请求方式有什么不同:


html中请求的script标签

看到了个后来加入的属性crossorigin="anonymous",添加这个属性是因为引入了监控系统,通过这个属性可以获取到资源内部详细的报错信息。对这个属性不熟悉的可以参考这里。

crossorigin="anonymous"

实践

有了以上猜测之后随即移除了aa.js等一切js静态资源的crossorigin属性,移除之后发现静态资源可以正常加载,查看下详细的网络请求返回的字段;

1.https://j1.cdn.com.cn/aa.js请求返回值response headersaccess-control-allow-origin:https://sh.yy.com

2.https://j1.cdn.com.cn/bb.js请求返回值response headersaccess-control-allow-origin:https://cd.yy.com

和报错时候返回的一致,但是浏览器并没有再报错了,资源可以正常请求;

结论

这个问题和cdn有关。

首先静态文件上到服务器之后,用户发出资源请求cdn会进行受理,这么多的cdn我们应该找哪一个呢,我们的DNS服务器会根据距离、响应时间等一系列策略为用户挑选一个反应速度最快最优的cdn来进行服务。

假设用户访问https://sh.yy.com页面,其中aa.js打到了甲cdn上之后,甲cdn发现资源没有缓存,便请求了最新的资源并进行缓存,bb.js打到了乙cdn上,乙cdn发现资源没有缓存,便请求了最新的资源并进行缓存,于是两个资源的response headers均为https://sh.yy.com.

之后用户又访问了https://cd.yy.com页面,其中aa.js依旧打到了在甲cdn上拿着上次的缓存走了(其中会有一系列缓存策略,不再展开),但是bb.js这次打到了丙cdn上,丙cdn一看资源没有缓存过,就获取了最新的资源并且进行缓存。此时这时候aa.js文件的response headers就是上次的https://sh.yy.com,由于丙cdn没有bb.js的缓存所以他取的资源就是最新的表现为response headers就是当前的https://cd.yy.com

如果我们在标签中添加了crossorigin="anonymous"属性,那么跨域请求的时候就会判断是否同源,如果不是同源的话,请求将会报错并无法正常处理response。

Q&A

在此,我们来一一解答下上面的疑问,找到答案的小伙伴这里可以略过啦;

Q1:为什么我这边几个开发的电脑都没有这种问题,但是产品却大概率有这问题,有时候刷新就恢复正常了?
A1:开发的时候会使用disable cache,此时浏览器将不会走本地缓存。产品的电脑一般都没有进行勾选,所以当切换域名时,请求的还都是从本地缓存中的资源。

Q2:为什么https://j1.cdn.com.cn/aa.jsresponse headers中的域名不是当前域名?
A2:综上,因为之前别的用户请求过该资源,cdn将其缓存住,所以请求https://j1.cdn.com.cn/aa.js的内容及response headers等都是缓存中的信息;

Q3:为什么同样是j1.cdn.com.cn域名下两个文件(aa.js和bb.js)的access-control-allow-origin却不一样?
A3:综上,因为这两个请求的资源是由不同的cdn返回的,而且有可能aa或bb文件之前有人访问过,导致缓存住了;

Q4:为什么浏览器的disable cache项可以影响到后端返回的response headers,以至于资源无法加载?
A4:并不是影响到了后端的返回,而是浏览器的机制。当查看到设置了crossorigin属性且请求返回值的access-control-allow-origin与当前域名不一致后抛出了错误,不再进行进一步渲染等操作;

Q5:这个浏览器所谓的response headers是由于资源请求有问题,浏览器自身机制直接取上次的值呢?
A5:这个上面有写到,使用curl或者抓包工具,可以看到后端返回的就是控制台network展示的这些信息。

有其他问题欢迎交流~