深入了解 CORS

最后更新:
阅读次数:

CORS(Cross-origin resource sharing)wiki,中文名为跨源资源共享,它是现代浏览器使用 Ajax 进行跨源通信的一种解决方案,现在已经被定为 W3C 标准。

原理

  • 简单地讲,CORS 就是基于 Ajax 并通过设置客户端和服务端的 HTTP Header 来实现跨域通信。

下面举例来说明。*_*

服务器地址:http://localhost/ajax/*
服务器存放 3 个测试文件

// 文件一:notAllow.php
<?php
echo 'hello percy';
?>

// 文件二:allowOne.php,这里只允许源为 http://blog.percymong.com 页面跨源请求
<?php
header("Access-Control-Allow-Origin: http://blog.percymong.com");
echo 'hello percy';
?>

// 文件三:allowAll.php,允许所有源的跨源请求
<?php
header("Access-Control-Allow-Origin: *");
echo 'hello percy';
?>
当前页面:http://blog.percymong.com/2016/08/13/ajax-introduction/index.html

// 打开浏览器控制端,运行以下函数
function runAjax(url) {
var request;
request = new XMLHttpRequest();

request.onreadystatechange = function() {
if (request.readyState == 4) {
if (request.status == 200) {
console.log(request.responseText);
}
}
};

request.open('GET', url);
request.send();
}

// 然后一一运行下面函数
runAjax('http://localhost/ajax/notAllow.php'); // 报错
runAjax('http://localhost/ajax/allowOne.php'); // 成功
runAjax('http://localhost/ajax/allowAll.php'); // 成功

// 接下来换一个页面,比如 http://www.ruanyifeng.com/blog/
// 控制端添加并运行最初的 runAjax 函数代码
// 然后再一一运行下面函数
runAjax('http://localhost/ajax/notAllow.php'); // 报错
runAjax('http://localhost/ajax/allowOne.php'); // 报错
runAjax('http://localhost/ajax/allowAll.php'); // 成功

运行上面的代码后,请自行在控制端下查看 HTTP 请求的详细信息。

发现一个有趣的事儿,我的博客地址在被请求后,服务器返回的响应头部都是自动添加了 Access-Control-Allow-Origin: *,这就意味着别的任何网站都可以对我的网站进行跨源请求。

当前页面:http://localhost/ajax/
// 运行下面代码
runAjax('http://blog.percymong.com'); // 控制端以字符串形式成功打印我博客首页的 HTML 文档

/***************************************************/

当前页面:http://www.baidu.com
// 运行下面代码
runAjax('http://blog.percymong.com'); // 控制端以字符串形式成功打印我博客首页的 HTML 文档

CORS work flowchart

一些和 CORS 机制相关的 HTTP 头字段

  • Request headers(HTTP 请求头)

    • Origin: origin URL
      • 标明发送请求的源(origin)
    • Access-Control-Request-Method: method
      • 在发出预检请求时带有这个头信息,告诉服务器在实际请求时会使用的请求方式
    • Access-Control-Request-Headers: field-name [, field-name]
      • 在发出预检请求时带有这个头信息,告诉服务器在实际请求时会携带的自定义头信息。如有多个,用逗号分开
  • Response headers(HTTP 响应头)

    • Access-Control-Allow-Origin: origin | *
      • 指定一个允许跨源向该服务器提交请求的 URI(一次只允许指定一个 origin)
    • Access-Control-Allow-Credentials: true | false
      • 若请求的 withCredientials 属性为 true ,则只有响应头有此字段并且该字段值为 true 时,响应才可以被得到
    • Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
      • 设置浏览器允许访问的服务器的头信息的白名单
    • Access-Control-Max-Age: delta-seconds
      • 这个头告诉我们这次预请求的结果的有效期是多久
    • Access-Control-Allow-Methods: method [, method ]
      • 指明资源可以被请求的方式有哪些(一个或者多个)
    • Access-Control-Allow-Headers: X-PINGOTHER
      • 用来指明在实际的请求中,可以使用哪些自定义 HTTP 请求头。如有多个,用逗号分开

以上这些 HTTP 头字段大部分还未使用过,各个字段的解释来自于 【MDN】HTTP 访问控制(CORS)

CORS 的两种请求

这里不再啰嗦,该小节的详细解释请参看后文的参考资料。下面是摘录的一部分内容。

  • 浏览器将 CORS 请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

  • 非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为”预检”请求(preflight)。 浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的 XMLHttpRequest 请求,否则就报错。

CORS VS. JSONP

  • JSONP 只能使用 GET 请求,而 CORS 能支持所有类型的 Http 请求。
  • CORS 可以令开发者使用普通的 XMLHttpRequest 来发起请求与获取数据,比 JSONP 有更好的错误处理机制。
  • JSONP 可以在不支持 CORS 的老旧浏览器上运作,不过现在绝大多数浏览器都已经支持 CORS 了。
  • 安全方面,JSONP 可能会导致 XXS 问题,而 CORS 通过允许网站手动地解析响应,以保证安全。

以上仅是个人粗略的了解,更详细解释请看 W3C Cross-Origin Resource Sharing

参考资料