同源策略:同協議,同域名,同端口。
跨域: 發起請求的域和資源所在的域不是同一個
CSRF(Cross-site request forgery): 原文链接: https://www.cnblogs.com/hyddd/archive/2009/04/09/1432744.html
解決跨域方法
一. JSONP
与XMLHttpRequest/Ajax方式不同,JSONP实际上只是调用外部服务器的脚本,从而可以执行本地服务的代码
優點:可以兼容老式瀏覽器,
缺點:只支持GET
请求,不能發送post,put,fetch,delete等請求
實現方式:
利用image, script, style等標簽没有跨域限制的特性,與外部資源通訊
從本地访问外部服务器上的代码脚本,帶上回調函數和其他参数,該回調函數的值是在本地中定義的函數
舉例:
http://xx.com/page1需要B服务器上面的新闻信息list
再page1中定义函数 showList
function showList (list) {
//....
}
通过<script src="http://b.com/getList.php?callback=showList&page=2" ></script>
getList.php中最终返回的是一段JS代码,这段代码中会调用 showList 这个回调函数
//...
getList().then((res) => {
showList(res)
})
//...
最终就完成了跨域访问,并且拿到了list的数据
二. CORS
CORS本质是在服务器端进行设置,
在前端他就和XMLHttpRequest一样,不同的是在后端serve的处理(比如后端设置Access-Control-Allow-Origin等)
在发出请求时,服务器端会返回响应信息,浏览器根据响应信息,判断请求是否成功,决定是否可以跨域访问该请求
非简单请求 和 简单请求的区别
简单请求
1. 请求类型: POST / GET / HEAD
&
2. 请求头信息不得超过以下几种:
Accept
content-type: application/x-www-form-urlencoded
、multipart/form-data
、text/plain
Accept-language:
content-language:
Last-Event-Id:
假如站点 http://foo.example 的网页应用想要访问 http://bar.other 的资源。http://foo.example 的网页中可能包含类似于下面的 JavaScript 代码:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/public-data/';
function callOtherDomain() {
if(invocation) {
invocation.open('GET', url, true);
invocation.onreadystatechange = handler;
invocation.send();
}
}
这里浏览器发出了一个请求,这个请求头的报文显示如下
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
服务器收到请求响应的报文如下
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
这里我们注意到请求报文中的orgin信息表示该请求来自 http://foo.example网站,浏览器根据请求去访问bar.other的服务器,响应报文中的 Access-Control-Allow-Origin: *表示该资源可以被任意的网站访问, 如果这个值指定了http://foo.example的话,表示他只能被foo.example访问。
如果Access-Control-Allow-Origin中指定的server没有foo.example的话,请求就会出错,抛出的错误会被XMLHttpRequest的onerror函数捕获,但这个时候请求响应的状态码可能还是200
Access-Control-Allow-Credentials: 是否允许请求时候带上cookie信息, 值为true表示允许, 不存在这个字段时候就表示拒绝带上cookie值. 但是也只有当发出请求设置withCredentials 为 true的时候,才能真正发送cookie信息给server
Access-Control-Expose-Headers: CORS请求时,XMLHttpRequest
对象的getResponseHeader()
方法只能拿到6个基本字段:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。如果想拿到其他字段,就必须在Access-Control-Expose-Headers
里面指定。上面的例子指定,getResponseHeader('FooBar')
可以返回FooBar
字段的值
非简单请求
非简单请求在发出真正的请求之前,会发送一次预请求(pre-flight request)给服务器,判断是否可以发送实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
满足下列条件之一的就是非简单请求
- 使用了下面任一 HTTP 方法:
- 人为设置除下列集合之外的其他首部字段:
Accept
Accept-Language
Content-Language
Content-Type
(but note the additional requirements below)DPR
Downlink
Save-Data
Viewport-Width
Width
-
Content-Type
的值不属于下列之一:application/x-www-form-urlencoded
multipart/form-data
text/plain
- 请求中的
XMLHttpRequestUpload
对象注册了任意多个事件监听器。 - 请求中使用了
ReadableStream
对象。
如下是一个需要执行预检请求的 HTTP 请求:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong'); //设置了其他的header头部信息
invocation.setRequestHeader('Content-Type', 'application/xml'); //设置了其他的Content-Type格式
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
上述代码使用POST方式发送了一个xml片段,并且自行定义了一个header头部字段 X-PINGOTHER,content-type的值设置为了application/xml格式
当要发送这个请求前就会先发送一个预请求。
server端会返回响应信息,响应头如下:
1.OPTIONS /resources/post-here/ HTTP/1.1 //请求方法为 OPTIONS 类型
2.Host: bar.other
3.User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
4.Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5.Accept-Language: en-us,en;q=0.5
6.Accept-Encoding: gzip,deflate
7.Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
8.Connection: keep-alive
9.Origin: http://foo.example //发送的请求来源
10.Access-Control-Request-Method: POST //告知服务器,实际请求将使用 POST 方法
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type //告知服务器,实际请求将携带两个自定义的头部信息,服务器据此决定,该实际请求是否被允许。
12.
13.
14.HTTP/1.1 200 OK
15.Date: Mon, 01 Dec 2008 01:15:39 GMT
16.Server: Apache/2.0.61 (Unix)
17.Access-Control-Allow-Origin: http://foo.example //表明接受来自foo.example的请求
18.Access-Control-Allow-Methods: POST, GET, OPTIONS //表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type
20.Access-Control-Max-Age: 86400 //预请求的缓存失效,在这个86400s内浏览器无须为同一请求再次发起预检请求
21.Vary: Accept-Encoding, Origin
22.Content-Encoding: gzip
23.Content-Length: 0
24.Keep-Alive: timeout=2, max=100
25.Connection: Keep-Alive
26.Content-Type: text/plain
三. postMessage
postMessage是一种跨源通信(cross-origin),当页面A和页面B (同源或者跨域)的协议相同(通常为https),端口(https端口号为443)相同,且页面中设置的document.remain(文档的原始域)相同的时候,可以进行通信。
postMessage是一种异步通信方式,他会再所有其他脚本都执行完成后(包括settimeout,ajax等)执行。
postMessage主要用在跨域通信,跨文档,多窗口之间进行通信
兼容性
IE兼容性不好(IE8 和 IE9 仅支持窗口与 <frame>
和 <iframe>
之间的通信, IE10使用有限制),其他浏览器兼容性良好,对于Safari浏览器,获取iframe对象的时候必须使用document.getElementId('your-frame').contentWindow,不能使用window.frames[index]模式
postMessage API
发送信息:otherWindow.postMessage(message, targetOrigin, transfer)
otherWindow: 要接受message的窗口对象,可以是iframe的值,window.open的返回值
message: 必填,向目标origin传递的信息, 考虑到兼容性等问题,一般格式为String, 所以再传递信息的时候通过JSON.Stringfy进行序列化
targetOrigin: 必填,*表示向任意网站发送信息, ‘/’ 表示同源网站, ‘https://xxx.com, https://xxx2.com’表示指定的网站
transfer: 可选,Transferable类型,代表一个能在不同可执行上下文中相互传递的对象
接受信息: window.addEventListener('message', eventHandler, false)
function eventHandler (event) {}
event是Message类型,
event.origin: 发送信息的窗口对象的origin
event.source: 对发送消息的窗口对象的引用; 您可以使用此来在具有不同origin的两个窗口之间建立双向通信。
event.data
使用场景和案例
1. iframe通信(同源/跨域)
2. 多窗口通信
3. WebWorker