出现跨域问题的原因及其解决方法
2018-01-11 本文已影响621人
Husbin
出现跨域问题的原因:
同源策略
同源策略
三同
- 协议相同
- 域名相同
- 端口相同
同源策略目的
- 保证用户信息安全,防止恶意网站窃取数据。同源策略是必须的,否则cookie可以共享。
同源策略的限制范围
- cookie、localstorage、indexdb无法读取。
- DOM无法获取。
- ajax请求不能发送。
规避策略
- cookie:设置
document.domain
共享cookie。 - DOM获取:(父子页面通信)H5引入了一个API,这个API为windows对象新增了一个
window.postMessage
方法,允许跨窗口通信,无论这两个窗口是否同源。
window.opener.postMessage(content,origin)
content是消息的具体内容,origin是
协议 + 域名 + 端口
- AJAX:
- JSONP:JSONP是服务器无客户端跨源通信的常用方法。基本思想是网页通过添加一个
<script>
元素,向服务器请求json数据,这种做法不受同源政策的限制,服务器收到请求后,将数据放在一个指定名字的回调函数里面传回来。(只能发GET请求) - WebSocket:WebSocket是一种通信协议。使用
ws://
(非加密)和wss://
(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。 - CORS:详解如下。
CORS
跨域资源共享(corss-origin resource sharing):CORS需要浏览器和服务器同时支持。目前所有浏览器都支持该功能,IE浏览器不能低于IE10。整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
两种请求
- 浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
1. 简单请求
- 对于简单请求,浏览器直接发出CORS请求。具体来说,就是在Header中增加一个
Origin
字段。如果浏览器发现跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin
字段。
GET /cors HTTP/1.1
Origin: http://easywork.xin //浏览器添加字段,说明本次请求来自哪个源(协议+域名+端口)。
Host: 119.23.214.114
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
-
服务器根据
Origin
的值决定是否同意这次请求。如果
Origin
指定的源在不在后端的许可白名单范围内,服务器会返回一个正常的http回应。浏览器接收后发现,这个response
的Header没有包含Access-Control-Allow-Origin
字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获。这种错误无法通过状态码识别,因此HTTP response的状态码有可能是200。如果
Origin
指定的域名在许可的范围内,则服务器返回的相应中,会多出几个头信息字段Access-Control-Allow-Origin: http://easywork.xin Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
-
Access-Control-Allow-Origin
:(必须字段)它的值要么是请求时Origin
的值,要么是*
,表示接受任意域名的请求。 -
Access-Control-Allow-Credentials
:(可选字段)它是一个bool值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发送到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials:true
;另一方面,前端必须在AJAX请求中打开withCredentials
属性:
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
- 注意:如果要发送Cookie,
Access-Control-Allow-Origin
不能设置为*
,必须指定明确的,与请求网页一致的域名。同时,Cookie依然遵守同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传。
- Access-Control-Expose-Headers
:(可选字段)CORS请求时,
XMLHttpRequest对象的
getResponseHeader(args)方法只能拿到6个基本字段:
Cache-Control、
Content-Language、
Content-Type、
Expires、
Last-Modified、
Pragma。如果想拿到其他字段,就必须在
Access-Control-Expose-Headers`里面指定。
-
2. 非简单请求
- 非简单请求是那种对服务器有特殊要求的请求,比如请求方法是
PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。 - 非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的
XMLHttpRequest
请求,否则就报错。
与JSONP比较
- CORS与JSONP的使用目的相同,但是比JSONP更强大。JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式的浏览器,以及可以向比支持CORS的网站请求数据。