什么是跨域?
JavaScript出于安全方面的考虑,不允许跨域调用其他页面的对象。通常来说,跨域分为以下几类:
在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。
CORS
通过XHR实现Ajax通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源。但是合理的跨域请求对开发某些浏览器应用程序至关重要。
CORS的背后基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求响应是应该成功还是应该失败。
比如:一个请求附加了一个额外的Origin头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。
1
Origin: http://www.example.com
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin头部中回发相同的源信息(如果是公共资源可以回发“*”),例如:
1
Access-Control-Allow-Origin: http://www.example.com
如果是没有这个头部,或者有这个头部但是源信息不匹配,浏览器就会驳回请求。
1. 处理一个简单请求
1
2
3
var url = 'http://api.bob.com/cors' ;
var xhr = createCORSRequest ( 'GET' , url );
xhr . send ();
这段javascirpt代码会发送一个GET请求,会伴随一个真实的浏览器HTTP请求被发出
1
2
3
4
5
6
7
HTTP request :
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” header,它的值由scheme(e.g. http/https), domain(e.g. bob.com), 和端口号(只被包含如果非default 80端口 e.g.81)组成。例如:www.bob.com
但是origin header的存在与否并不能说明它是不是一个跨域请求,因为有一些同域的请求也会包含这个header。比如,在firefox上同域请求是不包含origin的,但是在chrome和safari中put/post/delete同域请求包含origin header。浏览器并不期望同域请求的response里包含CORS response header。
所有和CORS相关的response header都是以“Access-Control-“为前缀的:
Access-Control-Allow-Origin(必须) 这个必须包含在所有合法的跨域请求的response中,其值要么是Origin header中的值,要么就是”*“允许任何域的请求。
Access-Control-Allow-Credentials(可选),默认情况下cookie是不包含在CORS请求中的,使用这个header将会指明要在CORS请求中包含cookie,它的有效值是true, 如果不需要cookie, 正确的做法不是将其值设为false, 而是根本就不要这个包含header.
Access-Control-Expose-Header(可选),XMLHttpRequest 2 object中有一个getResponseHeader()方法,用以返回特定的response header,但是它只能得到简单的响应header,如果想让客户端访问到其他的一些header, 必须设定这个 Access-Control-Expose-Header,它的值是以逗号分隔的你想暴漏给客户端的header。
2. 处理一个不简单的请求
除了GET之外,如果想使得请求具备譬如HTTP PUT/DELETE动作,或者支持Content-Type: application/json, 就需要去处理被我们称做是not-so-simple的请求。
1
2
3
4
var url = 'http://api.bob.com' ;
var xhr = createCORSRequest ( 'PUT' , url );
xhr . setRequestHeader ( 'X-Custom-Header' , 'value' );
xhr . send ();
Preflight Request
1
2
3
4
5
6
7
8
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 ...
CORS允许使用自定义header以及出了GET和POST以外的其他方法,不同body内容类型通过一种透明的服务器验证机制被称做是Preflighted Request,当试图发起真实请求的时候,”preflight“请求就已经先发送给服务器端。这种请求使用OPTIONS方法,同时也通常会附带一些额外的header,
Origin
Access-Control-Request-Method 请求希望使用的http方法
Access-Control-Request-Headers (可选)用逗号分隔的自定义header列表
例如:一个带有自定义header X-Custom-Header的PUT请求:
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
通过这个请求,服务器可以判断是否支持这种类型请求,服务器通过发送以下header的response进行响应:
1
2
3
4
Access - Control - Allow - Origin : http : //api.bob.com
Access - Control - Allow - Methods : POST , GET
Access - Control - Allow - Headers : X - Custom - Header
Access - Control - Max - Age : 1728000
Access-Control-Allow-Origin 同一般请求一样.
Access-Control-Allow-Methods 以逗号分隔的允许使用的方法列表
Access-Control-Allow-Headers 以逗号分隔的允许使用的header列表
Access-Control-Max-Age 以秒为单位的preflight的缓存时间
一旦Preflight Request给予许可,浏览器将会发起真实请求。
1
2
3
4
5
6
7
PUT / cors HTTP / 1.1
Origin : http : //api.bob.com
User - Agent : Mozilla / 5.0 …
Connection : keep - alive
Accept - Language : en - US
X - Custom - Header : value
Host : api . alice . com
真实的响应:
1
2
Access - Control - Allow - Origin : http : //api.bob.com
Content - Type : text / html ; charset = utf - 8
如果服务器想拒绝一个CORS请求,可以只返回一个不带CORS header的响应(HTTP 200)。服务器可能会拒绝不合法的preflight请求,因为没有专门的CORS header在响应中,浏览器会假定请求是不合法的,所以就不会发真实的请求。
Preflight Request
1
2
3
4
5
6
7
OPTIONS / cors HTTP / 1.1
Connection : keep - alive
Accept - Language : en - US
Host : api . alice . com
Access - Control - Request - Headers : X - Custom - Header
Access - Control - Request - Method : PUT
Origin : http : //api.bob.com
Preflight Response:
1
2
// ERROR - No CORS headers, this is an invalid request!
Content - Type : text / html ; charset = utf - 8
备注:$.support.cors将会被设置为true如果浏览器支持CORS。JQuery $.ajax()方法可以用于通常的XHR请求也可以用于CORS请求,但是它的实现当中不支持IE的XDomainRequest对象(IE8),但是有JQuery的插件去补充它[http://bugs.jquery.com/ticket/8283 ],