前端web:主流跨域方式有哪些?

GeekQiaQia發表於2019-04-19

主流跨域方式:cors 跨域 && jsonp 跨域

  • 不允許跨域的基本原因是:http請求頭中沒有允許跨域有關的引數。解決跨域請求的原理就是在攔截器中攔截請求,請在Http請求頭新增允許跨域的欄位

jsonp 跨域請求

  • jsonp的缺點就是隻能採用get請求。
    • 為了減輕web伺服器的負載,我們把js、css,img等靜態資源分離到另一臺獨立域名的伺服器上,在html頁面中再通過相應的標籤從不同域名下載入靜態資源,而被瀏覽器允許
    • 基於此原理,我們可以通過動態建立script,再請求一個帶參網址實現跨域通訊。
$.ajax({
                url: 'http://localhost:1112/getPerson?callback=?',
                dataType: 'jsonp',
                jsonpCallback: 'person',
                success: function(data){
                    $('.name').html(data.name);
                    $('.company').html(data.company);
                }
            })

複製程式碼

cors(cross-origin-resource-share) 跨域資源共享:

cors機制:

  • 它使用額外的HTTP頭來告訴瀏覽器,讓執行在一個origin上的web應用被准許訪問來自不同源伺服器上的指定資源。
  • 當一個資源從與該資源本身所在的伺服器不同的域/協議/埠請求一個資源的時候,資源會發起一個跨域請求;

舉例:

  • 站點http://domain-a.com 的某html頁面通過前端web:主流跨域方式有哪些? src 請求http://domain-b.com/imge.jpg;
  • 網路上的許多頁面載入來自不同域的css樣式表/影像/和指令碼等資源;

處於安全原因:

  • 瀏覽器限制從指令碼內發起http跨域請求,或者是跨站請求可以正常發起,但是返回結果被瀏覽器攔截了 例如:XMLHttpRequest 和 fetch發起的請求;
  • cors機制,允許web應用伺服器進行跨域訪問控制,從而是的跨域訪問資料傳輸得以安全進行;

API遵循同源策略:

  • 這意味著使用這些API的Web應用程式,只能從載入應用程式的同一個域請求HTTP資源,除非響應報文包含了正確CORS響應頭。

前端web:主流跨域方式有哪些?

新增CORS標準:

  • 跨域資源共享標準新增了一組 HTTP 首部欄位,允許伺服器宣告哪些源站通過瀏覽器有許可權訪問哪些資源;
  • 對那些可能對伺服器資料產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 型別的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。伺服器確認允許之後,才發起實際的 HTTP 請求。

cors舉例:

比如說,假如站點 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(); 
  }
}
複製程式碼

簡單請求:

不進行預檢請求的http請求,稱為簡單請求;

前端web:主流跨域方式有哪些?

Request:

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

Response:

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]
複製程式碼

預檢請求:

  • “需預檢的請求”要求必須首先使用 OPTIONS 方法發起一個預檢請求到伺服器,以獲知伺服器是否允許該實際請求。
  • "預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響。
預檢請求條件:
1.使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
2.人為設定了對 CORS 安全的首部欄位集合之外的其他首部欄位。該集合為:
Accept
Accept-Language
Content-Language
Content-Type (需要注意額外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
3.Content-Type 的值不屬於下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
4.請求中的XMLHttpRequestUpload 物件註冊了任意多個事件監聽器。
5.請求中使用了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');
      invocation.setRequestHeader('Content-Type', 'application/xml');
      invocation.onreadystatechange = handler;
      invocation.send(body); 
    }
}

......
複製程式碼

前端web:主流跨域方式有哪些?

Request:

 1.OPTIONS /resources/post-here/ HTTP/1.1
 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
11.Access-Control-Request-Headers: X-PINGOTHER, Content-Type

Response:

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
18.Access-Control-Allow-Methods: POST, GET, OPTIONS
19.Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
20.Access-Control-Max-Age: 86400
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
複製程式碼

預檢請求完成之後,傳送實際請求:

POST /resources/post-here/ 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
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Referer: http://foo.example/examples/preflightInvocation.html
Content-Length: 55
Origin: http://foo.example
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://foo.example
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

首部欄位 Access-Control-Max-Age 表明該響應的有效時間為 86400 秒,也就是 24 小時。在有效時間內,瀏覽器無須為同一請求再次發起預檢請求
複製程式碼

附帶身份憑證的請求:

Fetch 與 CORS 的一個有趣的特性是,可以基於 HTTP cookies 和 HTTP 認證資訊傳送身份憑證。一般而言,對於跨域 XMLHttpRequest 或 Fetch 請求,瀏覽器不會傳送身份憑證資訊。如果要傳送憑證資訊,需要設定 XMLHttpRequest 的某個特殊標誌位。

本例中,foo.example 的某指令碼向 bar.other 發起一個GET 請求,並設定 Cookies:

var invocation = new XMLHttpRequest(); var url = 'bar.other/resources/c…';

function callOtherDomain(){ if(invocation) { invocation.open('GET', url, true); invocation.withCredentials = true; invocation.onreadystatechange = handler; invocation.send();

} }

  • withCredentials 標誌設定為 true,從而向伺服器傳送 Cookies
  • 因為這是一個簡單 GET 請求,所以瀏覽器不會對其發起“預檢請求”。但是,如果伺服器端的響應中未攜帶 Access-Control-Allow-Credentials: true ,瀏覽器將不會把響應內容返回給請求的傳送者。

前端web:主流跨域方式有哪些?

附帶身份憑證的請求與萬用字元

  • 附帶身份憑證的請求,伺服器不得設定 Access-Control-Allow-Origin 的值為“*”。

這是因為請求的首部中攜帶了 Cookie 資訊,如果 Access-Control-Allow-Origin 的值為“*”,請求將會失敗。而將 Access-Control-Allow-Origin 的值設定為 foo.example,則請求將成功執行。

HTTP 響應首部欄位

Access-Control-Allow-Origin

響應首部中可以攜帶一個 Access-Control-Allow-Origin 欄位,其語法如下:

Access-Control-Allow-Origin: <origin> | *
複製程式碼

其中:

  • origin 引數的值指定了允許訪問該資源的外域 URI。對於不需要攜帶身份憑證的請求,伺服器可以指定該欄位的值為萬用字元,表示允許來自所有域的請求。

例如,下面的欄位值將允許來自 mozilla.com 的請求:


Access-Control-Allow-Origin: http://mozilla.com
複製程式碼

如果服務端指定了具體的域名而非“*”,那麼響應首部中的 Vary 欄位的值必須包含 Origin。這將告訴客戶端:伺服器對不同的源站返回不同的內容。

Access-Control-Allow-Credentials

  • Access-Control-Allow-Credentials頭指定了當瀏覽器的credentials設定為true時是否允許瀏覽器讀取response的內容。
  • 當用在對preflight預檢測請求的響應中時,它指定了實際的請求是否可以使用credentials。請注意:簡單 GET 請求不會被預檢;如果對此類請求的響應中不包含該欄位,這個響應將被忽略掉,並且瀏覽器也不會將相應內容返回給網頁。
Access-Control-Allow-Credentials: true
複製程式碼

HTTP 請求首部欄位

Origin

Origin 首部欄位表明預檢請求或實際請求的源站。

Origin: <origin>
複製程式碼

origin 引數的值為源站 URI。它不包含任何路徑資訊,只是伺服器名稱。

Access-Control-Request-Headers

Access-Control-Request-Headers 首部欄位用於預檢請求。其作用是,將實際請求所攜帶的首部欄位告訴伺服器。

Access-Control-Request-Headers: <field-name>[, <field-name>]*
複製程式碼

參考MDN

相關文章