原文: https://medium.com/@baphemot/understanding-cors-18ad6b478e2b前端
只要用过 AJAX,你应该就很熟悉浏览器控制台中出现的以下报错: node
当你看到这个信息,就意味着响应失败了;但你依然能在浏览器开发工具的网络 tab 里看到返回数据 -- 这是什么状况呢?webpack
你所观察到的这种行为是浏览器 CORS 实现机制的效果。在 CORS 成为标准以前,因为安全缘由,没有办法跨域调用 API。也就是(必定程度上依旧是)被所谓同源策略(Same-Origin Policy)限制住了。git
CORS 机制是为了在承认用户发起的请求的同时,阻止那些恶意 JS;并在如下状况发起的 HTTP 请求时被触发:github
这种机制阻止了当你已经登陆 www.yourbank.com 的状况下,攻击者在各类网站上植入的脚本(好比经过 Google Ads 显示的广告)向 www.yourbank.com 发起的携带 你的身份凭证 的 AJAX 请求。web
对于“简单的” GET 或 POST 请求,若是服务器没有对其做出携带特殊 HTTP 头部的响应 -- 请求依然被发送而且数据也照样被返回,但浏览器将不容许 Javascript 访问该响应。chrome
若是浏览器尝试着去弄一个“没那么简单”的请求(好比一个包含了 cookie 的请求,或 Content-type 不是 application/x-www-form-urlencoded
、multipart/form-data
、text-plain
三者之一的),则被称为预检(preflight)的机制将被用到,而且一个 OPTIONS 请求会被发往服务器。express
关于“没那么简单”的请求,一个常见的例子是在请求中加入 cookie 或自定义头部 -- 若是浏览器发送了这样的请求且服务器没有正确响应的话,则只有预检调用会发送(不包含额外的头部),而浏览器本应使用的真实的 HTTP 请求就不会被发送了。npm
Access-Control-Allow-
什么什么的...在 CORS 请求和响应中,都用到了一些 HTTP 头部,其中如下这几个是你必须理解的:后端
该头部是客户端发起的请求的一部分,包含了应用所在的域。因为安全缘由,浏览器不会容许用户重写这个值。
该头部只须要在服务器支持经过 cookie 认证的状况下出如今响应中。这种状况下,其惟一合法值就是 true。
一个逗号分隔的、表示服务器将会支持的 HTTP 请求动词(如 GET, POST)列表。
格式为一个逗号分隔的列表,表示服务器将会支持的请求头部值。若是使用了自定义头部(好比 x-authentication-token),则应该将其置于这个 ACA 头部(译注:即 Access-Control-Allow-Headers
)响应中,并返回到 OPTIONS 调用中;除非该请求被阻塞了。
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
复制代码
类似的是,该响应应包含一个头部列表,表示将在实际的响应中出现的值,并应在客户端中有效。全部其余头部则会被限制。
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
复制代码
首先要清楚的是,CORS 行为并不是一种错误 -- 这种机制致力于保护你的用户、你自己,或你调用的站点。
有时,缺乏合适的头部,会致使客户端的错误执行(如丢失了 API key 等认证信息)。
取决于你面临的场景,如下手段能够“搞定这种错误”:
这是最好的状况了 -- 你能根据调用,在服务器上实现合适的 CORS 响应。若是 API 用 node 的 express 实现,那么简单的使用 cors
包(译注:https://github.com/expressjs/cors)就能够了。若是要保证站点的适度安全,能够考虑为 Access-Control-Allow-Origin
设置一个白名单。
这是次优的状况,由于其实这就是手段 A,只是暂时性的受限。为了临时解决,可让浏览器忽略 CORS 机制 -- 好比使用 ACAO Chrome 扩展
(译注: 或指 Allow-Control-Allow-Origin: *
扩展) 或用以下参数在启动 Chrome 时彻底禁止 CORS:
chrome --disable-web-security --user-data-dir
复制代码
切记,这将禁止浏览器会话期间 全部 网站的 CORS 机制;要当心慎用。
另外的替代方法是使用 devServer.proxy
(假设你用到了 webpack 作开发);或使用一个 CORS-as-a-service
解决方案,好比 https://cors-anywhere.herokuapp.com/ 。
Ok,如今事儿大了。首先要搞清为何服务器没有发送适当的头部。
也许是不容许第三方应用访问其 API ?又或者其 API 只服务于服务器端而非浏览器?要么就是你须要在 URL 中发送认证令牌?
若是你依然认为能够经过浏览器访问数据,就得在浏览器应用和 API 之间编写本身的代理了,就相似于咱们在手段 B 中作的那样。
该代理没必要和你的应用运行在一样的域下,只要当代理自己和客户端通信时正确支持 CORS 就行。代理和 API 之间的通信就彻底没必要支持 CORS 了。
你既能够编写本身的平台,也可使用诸如 https://www.npmjs.com/package/cors-anywhere 的成熟方案。
要记住若是你须要支持身份凭证,这样的办法会引入一个安全风险。
若是但愿学习更多关于 CORS 的细节,推荐阅览这篇 MDN 文章 (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)。
长按二维码或搜索 fewelife 关注咱们哦