跨域

本文主要涉及三種跨域方法:JSONP、CORS、postMessage。javascript

Q:爲何會出現跨域問題?
A:出於瀏覽器的同源策略限制,瀏覽器會拒絕跨域請求。
*注:嚴格的說,瀏覽器並非拒絕全部的跨域請求,實際上拒絕的是跨域的讀操做。瀏覽器的同源限制策略是這樣執行的:php

  • 一般瀏覽器容許進行跨域寫操做(Cross-origin writes),如連接,重定向;
  • 一般瀏覽器容許跨域資源嵌入(Cross-origin embedding),如 img、script 標籤;
  • 一般瀏覽器不容許跨域讀操做(Cross-origin reads)。*

Q:什麼狀況纔算做跨域?
A:非同源請求,均爲跨域。名詞解釋:同源 —— 若是兩個頁面擁有相同的協議(protocol),端口(port)和主機(host),那麼這兩個頁面就屬於同一個源(origin)。
html

img01
img01

Q:爲何有跨域需求?
A:場景 —— 工程服務化後,不一樣職責的服務分散在不一樣的工程中,每每這些工程的域名是不一樣的,但一個需求可能須要對應到多個服務,這時便須要調用不一樣服務的接口,所以會出現跨域。html5

如何實現跨域

一般,最經常使用的跨域方式有如下三種:JSONP、CORS、postMessage。java

JSONP

單純地爲了實現跨域請求而創造的一個 trick。
【實現原理】
雖然由於同源策略的影響,不能經過XMLHttpRequest請求不一樣域上的數據(Cross-origin reads)。可是,在頁面上引入不一樣域上的js腳本文件倒是能夠的(Cross-origin embedding)。所以在js文件載入完畢以後,觸發回調,能夠將須要的data做爲參數傳入。
【實現方式(需先後端配合)】json

<script type="text/javascript">
    function dosomething(data){
        //處理得到的數據
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>複製代碼
<?php
$callback = $_GET['callback'];//獲得回調函數名
$data = array('a','b','c');//要返回的數據
echo $callback.'('.json_encode($data).')';//輸出
?>複製代碼

【JSONP的優缺點】
優勢:兼容性好(兼容低版本IE)
缺點:1.JSONP只支持GET請求; 2.XMLHttpRequest相對於JSONP有着更好的錯誤處理機制後端

CORS

CORS 是W3C 推薦的一種新的官方方案,能使服務器支持 XMLHttpRequest 的跨域請求。CORS 實現起來很是方便,只須要增長一些 HTTP 頭,讓服務器能聲明容許的訪問來源。跨域

值得注意的是,一般使用CORS時,異步請求會被分爲簡單請求和非簡單請求,非簡單請求的區別是會先發一次預檢請求。
【簡單請求】
使用下列方法之一且沒有人爲設置對 CORS 安全的首部字段集合以外的其餘首部字段:瀏覽器

  • GET
  • HEAD
  • POST
    - 僅當POST方法的Content-Type值等於下列之一纔算做簡單請求
           - text/plain
           - multipart/form-data
           - application/x-www-form-urlencoded複製代碼

請求報文:安全

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複製代碼

請求報文的第10行:Origin: foo.example 代表該請求來源於 foo.exmaple。
響應報文:

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
[XML Data]複製代碼

響應報文的第4行:Access-Control-Allow-Origin: * 代表該資源能夠被任意外域訪問。

【非簡單請求】

  1. 使用了下面任一 HTTP 方法:
  • PUT
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE
  • PATCH
  1. 人爲設置了對 CORS 安全的首部字段集合以外的其餘首部字段。該集合爲:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (but note the additional requirements below)
  • DPR
  • Downlink
  • Save-Data
  • Viewport-Width
  • Width
  1. Content-Type 的值不屬於下列之一:
  • application/x-www-form-urlencoded
  • multipart/form-data
  • text/plain

發送真正請求前會先發送預檢請求,如圖所示:

img02
img02

1.第一條OPTIONS爲預檢請求,中同時攜帶了下面兩個首部字段:

Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER複製代碼

預檢請求的Request中的Access-Control-Request-Method: POST,是告訴服務器,以後的實際請求將使用POST方式。
Access-Control-Request-Headers 是告訴服務器,實際請求將攜帶兩個自定義請求首部字段:X-PINGOTHER 與 Content-Type。服務器據此決定,該實際請求是否被容許

預檢請求的Response中的

Access-Control-Allow-Origin: foo.example // 標識可接受的跨域請求源;
Access-Control-Allow-Methods: POST, GET, OPTIONS //標識可接受的跨域請求方法,如GET、POST、OPTIONS;
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type //標識可接受的跨域請求自定義頭;
Access-Control-Max-Age: 86400。 //標識本次預請求的有效時間(秒),期間內無需再發送預請求;

XMLHttpRequest 請求能夠發送憑證請求(HTTP Cookies 和驗證信息),一般不會跨域發送憑證信息,但也有一些狀況須要打通不一樣的登陸態,所以若是要發送憑證信息,須要設置 XMLHttpRequest 的某個特殊標誌位。好比下面代碼,能夠把 XMLHttpRequest 的 withCredentials 設置爲 true,這樣瀏覽器就能跨域發送憑證信息。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;複製代碼

服務端返回的響應頭中的 Access-Control-Allow-Credentials 字段存在且爲 true 時,瀏覽器纔會將響應結果傳遞給客戶端程序。另外,Access-Control-Allow-Origin 必須指定請求源的域名,不然響應失敗。

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://foo.com
Access-Control-Allow-Credentials: true
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain複製代碼

以下圖所示爲附帶身份憑證的請求流程圖:

img03
img03

postMessage

window.postMessage(message,targetOrigin) 方法是html5新引進的特性,可使用它來向其它的window對象發送消息,不管這個window對象是屬於同源或不一樣源,目前IE8+、FireFox、Chrome、Opera等瀏覽器都已經支持window.postMessage方法。

otherWindow.postMessage(message, targetOrigin, [transfer]);複製代碼
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息