跨域请求错误问题(CORS问题)探究小记
跨域请求错误问题,算是一个常见但又不是100%遇得到的问题。简单的说会在浏览器控制台中,看到有关资源(包括且不限于图片、字体等)请求失败的提示,一般会提到原因是“CORS Policy”。
这个问题是怎么遇到的,话题要说回去年网站迁移图床大调整。当时发现页面中一些引用图床的文件不加载(以图片和字体为主),但直接访问这些文件的地址又是正常的。
找了一圈后,在Chrome控制台中看到了错误提示,资源加载失败。
现象加上错误提示后,已经能定位出问题了,搜索一番知道了是遇到了跨域请求错误问题(也叫CORS问题),后面想办法修复了。
但是当时赶时间,解决报错后也没有深入的去研究这个到底是怎么一回事。最近稍微空下来了弄弄代码,又想起了这个问题,就跑去查清楚资料搞明白这个问题。说起来也好久都没写文章了(无限拖延制),正好随便写写好了2333333
提到这个问题,不得不先说一个浏览器中的一个安全策略:CORS,中文叫跨域资源共享标准(Cross-Origin Resource Sharing),有时也被称为同源策略,是浏览器在处理页面内引用了不同地方的资源时使用的一种机制(策略)。
在火狐开发者的官网能查到对CORS的详细解释。
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
比如,站点 http:// domain-a.com 的某 HTML 页面通过 <img> 的 src 请求 http:// domain-b.com/image.jpg。网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
嗯,读起来很像技术大佬写的术语汇总……总之看着很懵的话,来个简化版的。
假设说站点A(域名a.com)的页面,引用了另一台服务器B(域名b.com)中的某些图片、字体等。因为有CORS策略,浏览器就会默认请求另一个服务器的资源是不安全的,所以拒绝去加载来自b.com下的资源。
个人认为这里面有三点值得注意的地方。一是对“引用”另一台服务器的资源的定义;按照解释的描述,使用JS异步请求(也就是AJAX技术)或者是调用Fetch API,都属于“引用”。
第二点是,什么样的情况算是访问另一台服务器?根据详细解释,可以明确的是,被加载的资源网址域名不同、域名指向的服务器不同(就是说物理上的两台服务器)、协议不同(例如http与https)、访问的端口不同,都算是访问另一台服务器。对于域名的话,似乎更复杂些,一二级域名都必须相同才不算跨域。关于同源的准确说明,同样能在火狐的页面找到:《浏览器的同源策略》
另外,从Mozilla解释的机制上来说,并不一定是浏览器限制了发起跨站请求,也可能是跨站请求可以正常发起,但是返回结果被浏览器拦截了(也就是简化版中统称为的“拒绝加载”)。
为什么浏览器要引入这个策略呢?简单的说就是安全原因,跨站请求有可能会引起CSRF(Cross-site request forgery,中文:跨站请求伪造)攻击。不过这不是重点,这里就不深入讨论了……
那么怎么解决这个问题呢?Mozilla其实已经解释清楚了,需要“响应报文包含了正确CORS响应头”。
了解HTTP的人都会知道,每次HTTP请求发送的数据(也就是术语所说的“报文”),在头部都会包含一些固定的信息(术语叫字段)。CORS作为新的 W3C 标准,在HTTP头部添加了一些字段,用来让被访问的服务器声明哪些来源站有权限访问哪些资源。如下图“access-control”开头的字段
换句话说,其实不是要改浏览器或原页面,而是要从被请求的外部服务器(图床)入手。给另一台服务器的响应头部(header)中添加一些信息,告诉浏览器这些资源文件可以被引用来源站点“安全”的使用,浏览器就会正常加载这些资源了,这样就不会发生跨域请求错误。
(其实我也不理解为什么要把策略要设计成这样,但确实是要这样解决……-_-||)
提一句题外话,实际上跨站请求错误的问题还有其他途径解决,但基本都是针对开发的角度入手的。这里就只用CORS标准本身来完成吧。
这里图床的服务器是Nginx环境的,所以用编辑器打开图床站点的网站配置文件,在解析规则中加入下列内容即可。
location ~ .*\.(js|css|ttf|woff|woff2)?$ { add_header 'Access-Control-Allow-Origin' *; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,X-Requested-With'; add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE'; }
“add_header”作用是为HTTP请求(响应)添加头部信息。其中"Access-Control-Allow-Credentials"这一项是用来让跨域请求携带Cookie信息的,如果去掉默认不会携带。
第一行是Nginx用来匹配对应的文件,括号中就是对应的资源文件的扩展名(例如:ttf),有多个就用“|”符合分开。
这里没有包含图片和其他类型媒体,是因为并没有为这些文件加入CORS的头部内容,而且分开放在防盗链的部分了。
把上述内容插入到适当的位置,看起来大概是这样的。
修改后不要忘记用“service nginx reload”命令重新加载配置文件。
另外还有一点,目前无法确定的地方。按照前文中Mozilla的解释,纯HTML的引用(例如通过<img>图片标签显示图片),应该仍然符合跨域共享这个机制的约束条件内。
但是后面测试过发现,在图床(另一台)服务器上即使没有做任何调整,页面的<img>标签中src引用跨站图片,浏览器还是能正常显示,没有触发跨域请求错误。
查了一些资料也没有发现对这一点的直接解释,网上搜到的很多内容也没有明确的说不通过JS只有HTML引用的情况是否属于CORS机制的约束范围内。结合开发的经验来看,估计是除了通过JavaScript去访问跨站资源,纯静态的引用应该不会导致浏览器因为CORS策略不加载图片吧……
https://xyuxf.com/archives/1799
欢迎关注 咸鱼先锋 (微信号公众号:xyuxf),获取干货推送
共有 0 条评论