你应该了解的CORS

更新日期: 2019-07-21阅读: 2.1k标签: 跨域

如果你和我一样,第一次遇到 CORS (跨域资源共享),你想让服务器接收那些你拼接的 Ajax 请求并处理他们。所以你去 stackoverflow.com 复制一段代码来设置一些 HTTP Headers ,让请求可以正常工作。但是,可能还有一些事情你应该知道。


CORS 是什么,不是什么

新手通常混淆的原因,就是因为他们并不明确 CORS 能做什么。首先, CORS 并不是一种安全措施,实际上恰恰相反: CORS 是一种绕过 “同源策略(SOP)” 的方法。同源策略是一种安全措施,阻止您向其他域发出 nameAjaxname 请求。

同源策略声明一个域上的网站,无法向另一个域发出 XMLHttpRequest(XHR) 请求。这可以防止恶意网站向已知网站(比如 Facebook 或者 Google)发出请求,改变用户的登录状态,以便可以冒充其他用户。此策略由浏览器实现(所有浏览器都实现了同源策略,尽管实现细节上存在细微的差别),这意味着此策略并不适用于从服务器,或者任何其他HTTP客户端(比如 cURL , postman )发出的请求。此外,服务器同样无法完全控制它:服务器将处理每个请求,并假设他们都来自可信域,请求是否会被阻止完全取决于浏览器。

同源策略绝不意味着防止攻击者向您的服务器发出请求(因为攻击者显然不会使用浏览器)。它只是为了防止合法用户在使用知名浏览器浏览网站时,在不知情的情况下,向你的网站发起请求。

CORS是一种绕过同源策略的方法,在某些情况下,您希望一些特殊的站点可以向你的服务器发起请求,即使正常情况下它会被阻止。(通常,是允许您的前端应用向您的api发出请求)。


CORS 是如何工作的

与 HTTP 的相同, CORS 基本上也是浏览器和服务器之间的对话。假设你前端的域名为 namedomain-a.comname ,后端API的域名为 namedomain-b.comname ,对话会是这样的:

  • 浏览器 :“Hey namedomain-b.comname , namedomain-a.comname 上的这个脚本要向你发起一次 nameAjaxname 请求,但是我应该阻止它,除非你告诉我这个请求是没问题的。”
  • 服务器 :“我不知道,但是我可以告诉你, namehttps://domain-a.comname 只允许发送 GET,POST,OPTIONS 和 DELETE 请求,并且需要每10分钟验证一次。”

浏览器 想了想:“ yeah,这是个正确的域名,我应该给他发送请求。”

  • 浏览器 :“Hey namedomain-b.comname ,我想在这个终端向你发送 POST 请求。”
  • 服务器 :“没问题,这是你的 name200name ”

或者,如果用户位于不同的域,则对话会更短:

  • 浏览器 :“Hey namedomain-b.comname , namemalicious-domain.comname (恶意站点)上的这个脚本要向你发起一次 nameAjaxname 请求,但是我应该阻止它,除非你告诉我这个请求是没问题的。”
  • 服务器 :“我不知道,但是我可以告诉你, namehttps://domain-a.comname 只允许发送 GET,POST,OPTIONS 和 DELETE 请求,并且需要每10分钟验证一次。”

浏览器 想了想:“ Oh,这不是正确的域名,我们最好不要发送请求”,然后在控制台打印了错误。

译注:第二种,使用开发者工具查看时,看不到 Response Headers,但是可以看到下图中的提示:


Node CORS 测试地址


在浏览器中的样子

在上面的小场景中,浏览器提出的第一个问题称为 预检请求 ,对应的 HTTP 谓词是 nameOPTIONSname。遇到这种预检请求,服务器应该总是返回一个 200 的响应,没有正文,但是会包含 nameAccess-Control-Allow-Originname ,以及一些其他请求头。在我们的示例中请求头如下:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://domain-a.com
Access-Control-Allow-Methods: GET, POST, OPTIONS, DELETE
Access-Control-Max-Age: 3600

它告诉浏览器,它只能响应来自 namedomain-a.comname 的请求,可以处理 GET, POST, OPTIONS 或者 DELETE 请求(PUT 请求会被阻止),并且他可以缓存此信息 3600 秒,所以它不需要都发起一个新的 nameOPTIONSname 请求。


当然,如果我们使用其他域名,这将不起作用。浏览器会发送 nameOPTIONSname 请求,然后在控制台中抛出异常,并且永远不会发送 namePOSTname 请求。


很直接,对吧?

但是,也存在一些陷阱...


关于 CORS 的棘手问题

所求请求都包含 CORS 头( nameheadersname )

您可能会认为,如果您的服务器响应 nameOPTIONSname 请求时返回 200,然后你将这些正确的请求头去掉。然后你将看到浏览器先发送了 nameOPTIONSname 请求,然后发送了其他请求,其他请求挂掉了... 这是因为每个请求(GET, POST, 或者其他请求)都应该包含相同的请求头:“Access-Control-Allow-Headers”。


并非所有请求都会触发预检请求

有一些请求不会触发预检请求,比如 GET 请求,或者 nameContent-Typename 设置为 nameapplication/x-www-form-urlencodedname 的 POST 请求。这些是浏览器一直允许的“简单请求”,(即使在CORS不支持的情况下,你依然可以使用超链接( nameaname 标签)或者使用 POST 请求向其他网站提供表单,您可以在 此处 找到完整列表。在 POST 请求的情况下,结果会有些违反直觉:浏览器将发出 POST 请求(因此您的服务器可能会保留一些数据),然后忽略响应。

在传统的Web应用程序中,您可以使用 nameapplication/jsonname 作为 namecontent-typename ,因此会有预检请求,但请记住,您的服务器可能仍会收到来自其他域的 POST 请求,因此请不要盲目接受它们。


被允许的域名必须包含协议

您不能只将 namemydomain.comname 当做域名使用,它还需要包含协议,(例如: namehttps://mydomain.comname )。有趣的是,你不能同事接收 namehttpname 和 namehttpsname ,因为......


您只能允许一个域

您可以使用 nameAccess-Control-Allow-Origin: *name 来允许每个域访问,也可以只允许一个域访问。这意味着如果您需要多个域来访问您的 nameAPIname 时,您需要自己处理它。

处理此问题的最简单方法是在服务器上维护允许访问的域列表,如果域位于该列表中,则动态的改变响应头的内容。下面是一个 php 的例子:

$allowedDomains = [
    "http://www.mydomain.com",
    "https://www.mydomain.com",
    "http://www.myotherdomain.com",
    "http://www.myotherdomain.com",
];

$originDomain = $_SERVER['HTTP_ORIGIN'];

if (in_array($originDomain, $allowedDomains)) {
    header("Access-Control-Allow-Origin: $originDomain");
};

或者 Node.js 的例子(改编自这个 stackoverflow 答案 )

app.use(function(req, res, next) {
  const allowedOrigins = [
    "http://www.mydomain.com",
    "https://www.mydomain.com",
    "http://www.myotherdomain.com",
    "http://www.myotherdomain.com",
  ];
  const origin = req.headers.origin;
  if(allowedOrigins.indexOf(origin) > -1){
    res.setHeader('Access-Control-Allow-Origin', origin);
  }
  return next();
});


同源策略适用于 Chrome 和 Safari 的文件系统,不适用于 Firefox 的

如果您向本地文件发出请求,Firefox会认为它始终位于同一个域上并允许该请求。基于 Webkit的浏览器(如Chrome或Safari)会将此视为安全风险,并阻止对本地文件的 nameAjaxname 查询。解决这个问题的唯一方法是使用Firefox,或安装将发送 nameAccess-Control-Allow-Origin: *name 请求头的Web服务器。

正如 @brianjenkins94 在评论中指出的那样,您也可以用 name--disable-web-securityname 参数来启动Chrome 。


iOS WKWebview需要CORS

如果您正在开发使用 namewebviewname (使用Cordova或Ionic)的移动应用程序,Android将不会给您带来任何麻烦,但iOS上的新 nameWKWebviewname 将需要CORS。这意味着您几乎必须始终将 nameAccess-Control-Allow-Originname 标头设置为 name*name ,但实际上这并不理想。

另一个选择是不在您的应用程序中发出 nameAjaxname 请求并使用 namecordovaname 插件来生成本机 namehttpname 请求,这将很方便的绕过同源策略。

谢谢阅读 !如果您想要更深入地了解CORS,请访问MDN:https://developer.mozilla.org

原文: What you should know about CORS
作者:Nicolas Bailly
译者:博轩


链接: https://www.fly63.com/article/detial/4274

web网站ajax跨域方法总结与解决方案

web前端的同学如果使用ajax,如果请求后端地址和页面访问的地址“URL的首部”不同就会出现访问被拒绝的情况,这就需要跨域来解决前后端的通讯问题,下面将列举一些在实际项目中应用也是比较多的方法。

使用CSS3特性做跨域,跨域还可以这样玩

利用js动态创建一个link插入到文档中, 请求css文件,利用 computedStyle = window.getComputedStyle 获取指定元素的 style 对象,利用 computedStyle .content 获取内容

Js通过Web代理发起跨域请求

XMLHttpRequest对象(IE中为XMLHTTP对象)是AJAX应用的核心。由于现代浏览器对跨域请求的限制,在使用时需多加注意。本教程尽量用简单易懂的话描述(跨域)问题,并提供一个方案:通过Web代理(Web Proxy)

jsonp跨域原理,jsonp和ajax的区别?

Jsonp原理是动态创建一个script标签,利用script标签src属性访问没有限制,实现跨域。Ajax是页面无刷新请求数据操作,Jsonp不是真正的ajax请求。讲解Ajax 和 jsonp的区别,在jquery中使用jsonp

JSONP技术栈_jsonp的使用

JSONP方案:前端提供 API 的方法,其实解耦还没有解的很干净,我们在设置script的src时直接给它传参,后端插入这个参数这就可以了。因为JSONP是通过动态创建Script实现的,动态创建Script只有GET请求没有POST请求

CORS跨域请求

浏览器的同源策略会导致跨域,这里同源策略又分为以下两种:DOM同源策略、XmlHttpRequest同源策略。只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。

如何用Nginx解决前端跨域问题?

在开发静态页面时,类似Vue的应用,我们常会调用一些接口,这些接口极可能是跨域,然后浏览器就会报cross-origin问题不给调。最简单的解决方法,就是把浏览器设为忽略安全问题,设置--disable-web-security。

vue-cli3 中跨域解决方案

此方案只能用于开发环境,线上最好设置同源策略(遇到个后端,装你妈批),前后端不在同一服务器的情况下,前端要访问后端API,可通过在vue.config.js中配置代理服务器。

vue项目打包后怎样优雅的解决跨域

在使用vue.js开发前端项目时,再结合webpack搞起各种依赖、各种插件进行开发,无疑给前端开发带来了很多便捷,就在解决跨域这个问题上,相信众多用vue.js的前端同僚们同我一样尝到了甜头

nginx 反向代理处理跨域问题

正向代理是你发出请求的时候先经过代理服务器,所以实际上发出请求的是代理服务器。反向代理是“代理你的目标服务器”,请求目标服务器的代理,做一些处理后再真正请求。在这篇文章里,反向代理用于处理跨域问题。

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!