跨域资源共享的使用

前言

页面中常常会有需要跨域通信的需求实现,我们知道浏览器的同源策略是不允许不同域之间的相互通信的(这里不深究域的定义及如何才算跨域),比如a.com有b.com想要的数据,那么在b.com页面中发送ajax请求到a.com是不允许的,相信大家都知道一些跨域通信的实现方法:

  • JSON-P(安全性不好)
  • window.name + iframe(实现的方式恶心)
  • window.postMessage(HTML5)
  • proxy(麻烦的部署及维护)
  • ...

跨域资源共享(Cross-Origin Resource Sharing)是W3C的一项规定,它规定了在浏览器中,基于XMLHttpRequest对象的跨域请求通信的原理,基本上保持了原有对象的用法。

CORS需要服务器端及客户端双方面的更改支持。本文主要介绍如何发起一个跨域请求和如何在服务器端支持CORS。

兼容性

  • Chrome 3+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Internet Explorer 8+

发起一个跨域请求

  1. 第一步新建XMLHttpRequest对象
function get_CORS_XHR(method, url) {
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr) {
        // "withCredentials"属性只存在于XMLHttpRequest2对象中
        // Chrome, Firefox, Opera and Safari
        xhr.open(method, url, true);

    } else if (typeof XDomainRequest != "undefined") {
        // XDomainRequest对象,兼容IE
        xhr = new XDomainRequest():
        xhr.open(method, url);

    } else {
        xhr = null;

    }
    return xhr;
}

var xhr = get_CORS_XHR('GET', 'http://example.com');
  1. 事件处理

XMLHttpRequest2对象新增了许多事件类型,原先的对象只支持onreadystatechange,新增事件有: (*星号代表IE不支持)

  • onloadstart*(请求开始发送)
  • onprogress(加载和发送数据中)
  • onabort*(实例abort方法被调用)
  • onerror(请求失败)
  • onload(请求成功)
  • ontimeout(请求超时)
  • onloadend(请求完成,不管失败还是结束)
// 通常我们处理最多的就是
xhr.onload = function() {
    var responseText = xhr.responseText;
    console.log(responseText);
    // ...
};

xhr.onerror = function() {...};
  1. withCredentials & 发送

默认情况下,标准的CORS请求是不会发送任何cookie信息的。如果你想让请求带上对应域的cookies信息(需要server支持),那么你需要:

xhr.withCredentials = true;
// 响应报文头部加上 Access-Control-Allow-Credentials: true

// handlers ...
xhr.send();

Server跨域请求处理支持

请求分类

可以给跨域请求分个类:

  1. 简单请求

符合下列要求的请求可以说是简单请求:

- HTTP Method
    - HEAD
    - GET
    - POST
- HTTP Headers
    - Accept
    - Accept-Language
    - Content-Language
    - Last-Event-ID
    - Content-Type
        - application/x-www-form-urlencoded
        - multipart/form-data
        - text/plain
  1. 不太简单的请求(A term by Monsur Hossain)

不符合(1)中的条件的请求

浏览器如Chrome, Firefox等会在不太简单的CORS请求发送前,为安全性考虑先发送一条”preflighted”OPTIONS请求

服务器端的处理根据请求的复杂程度处理方式有所不同。

处理简单请求

让我们举个栗子,CORS指定头部为粗体: Javascript:

var url = 'http://api.alice.com/cors';
var xhr = get_CORS_XHR('GET', url);
xhr.send();

HTTP 请求:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

值得注意的是CORS请求中必定包含Origin头部,但是包含此头部不一定意味着这个请求就是CORS请求。一个成功的跨域请求的响应报文可以是:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

所有CORS相关的头部均以Access-Control-开头,下面是关于各个头部的细节:

  • Access-Control-Allow-Origin(required) 此头部必须添加到响应报文中 ,不然缺省值会导致CORS请求失败。你可以设置*值让所有站点都可以访问你的数据,但最好还是控制一下
  • Access-Control-Allow-Credentials(optional) 设置此头部的值为true,如果你想要请求附带cookies。与上文提到的withCredentials属性协作。若此头部值为true而withCredentials属性为false,会导致请求失败,反之亦然
  • Access-Control-Allow-Expose-Headers(optional) XMLHttpRequest2对象存在getResponseHeader方法,允许访问一些简单的响应头部如:Content-Type,Cache-Control等等。如果想暴露一些特殊的头部,可以在此头部的值设置以逗号分隔的头部名称
处理不太简单的请求

如上文所说,处理不太简单的请求时,浏览器会先发出一次preflighted的请求,得到服务器允许后才执行真正的跨域请求,preflighted请求的结果会被缓存,多条请求同一服务器的跨域请求只会发送一次preflighted请求。举个栗子: Javascript:

var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader(
    'X-Custom-Header', 'value');
xhr.send();

Preflight请求:

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
  • Access-Control-Request-Method 真实的请求方法,在此为PUT。所有的Preflight请求都应该包含此头部
  • Access-Control-Request-Headers 值是以逗号分隔的头部名称,代表请求附带的其余头部

Preflight响应:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
  • Access-Control-Allow-Origin(同上文)
  • Access-Control-Allow-Credentials(同上文)
  • Access-Control-Allow-Methods(required) 允许跨域请求的请求类型,以逗号分隔。由于preflight响应可能被缓存,所以此头部设置会有所帮助
  • Access-Control-Allow-Headers 当请求中有Access-Control-Request-Headers头部时,此响应头说明服务器支持的头部,以逗号分隔
  • Access-Control-Max-Age(required) 指定preflight响应可以被缓存的时间,单位秒

真实的请求跟响应就可以正常发送接收了。如果服务器对preflight请求直接返回HTTP 200,不包含任何CORS指定的头部,那么这个跨域请求就会失败,触发onerror事件。控制台中会输出类似一下的报错信息:

XMLHttpRequest cannot load http://api.alice.com. Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

Server处理流程图

注: 最好在服务器端的Access-Control-Allow-Methods头部加上OPTIONS方法

Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://qunedu.oa.com

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏美码师

补习系列-springboot中的几种Scope

HTTP 头(Header)是一种附加内容,独立于请求内容和响应内容。 HTTP 协议中的大量特性都通过Header信息交互来实现,比如内容编解码、缓存、连接保...

562
来自专栏Java帮帮-微信公众号-技术文章全总结

Java全栈开发Spring学习第三天

今日内容 l Spring的事务管理 l 三大框架整合 1.1 上次课内容回顾: Spring整合WEB项目: * 需要配置一个监听器:ContextLoade...

3398
来自专栏互扯程序

跨域请求方案 终极版

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

1063
来自专栏Ryan Miao

重定向Http status code 303 和 302

http 302 http 303 Http 302 302是一个普通的重定向代码。直观的看来是,请求者(浏览器或者模拟http请求)发起一个请求,然后服务端重...

3055
来自专栏Java3y

AJAX跨域完全讲解

AJAX跨域完全讲解 今天在慕课网上学习了AJAX跨域完全讲解:https://www.imooc.com/learn/947 我在收集AJAX面试题的时候其实...

2617
来自专栏软件开发

JavaScript学习总结(二)——延迟对象、跨域、模板引擎、弹出层、AJAX示例

一、AJAX示例 AJAX全称为“Asynchronous JavaScript And XML”(异步JavaScript和XML) 是指一种创建交互式网页应...

1895
来自专栏阮一峰的网络日志

跨域资源共享 CORS 详解

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。 它允许浏览器向跨源服务器,发出XMLHttpR...

2827
来自专栏Web项目聚集地

什么是跨域?解决方案有哪些?

同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同...

732
来自专栏阮一峰的网络日志

浏览器同源政策及其规避方法

浏览器安全的基石是"同源政策"(same-origin policy)。很多开发者都知道这一点,但了解得不全面。 本文详细介绍"同源政策"的各个方面,以及如何规...

4056
来自专栏A周立SpringCloud

跨域访问支持(Spring Boot、Nginx、浏览器)

最近家中事多,好久没有写点啥了。一时间竟然不知从何说起。先说下最近家里发生的事情吧: 老爸肺气肿住院; 老妈甲状腺囊肿 儿子喘息性支气管炎住院 我莫名其妙尿脓。...

3805

扫码关注云+社区