AJAX 跨源 HTTP 請求
背景
跨源HTTP請求(也稱跨域AJAX請求)是大多數Web開發人員可能遇到的一個問題,根據同源策略,瀏覽器將限制客戶端的JavaScript在一個安全沙箱內,通常JS不能直接同一臺不同的域的遠端伺服器通訊。在過去,開發者們創造了許多解決方法以實現跨域資源請求,常用的方法如下:
-
使用Flash/Silverlight或伺服器端“代理”來與遠端通訊
-
在iframe中嵌入遠端伺服器並通過fragment或window.name通訊,參考這裡。
如此等等..
這些解決方法或多或少都有問題,比如使用JSONP時若只是簡單的“eval”將導致安全漏洞,#3雖然能用,但兩個域間必須依據嚴格的協議,恕我直言它既不靈活也不優雅
W3C已經引入了跨域資源共享 (CORS)作為能夠解決該問題並提供安全、靈活以及推薦標準的解決方案。
機制
從較高的層次來看我們可以簡單認為CORS 是介於 域A客戶端 的AJAX呼叫 和一個託管在域B的頁面 之間的契約, 一個典型的跨源 請求或者響應將會是這樣:
域 A 的 AJAX 請求頭
Host DomainB.com
User-Agent Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.0) Gecko/20100101 Firefox/4.0
Accept text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,application/json
Accept-Language en-us;
Accept-Encoding gzip, deflate
Keep-Alive 115
Origin http://DomainA.com
域 B 的 響應頭
Cache-Control private /> Content-Type application/json; charset=utf-8
Access-Control-Allow-Origin DomainA.com
Content-Length 87
Proxy-Connection Keep-Alive
Connection Keep-Alive
我上面標記的藍色部分是關鍵實現, "Origin" 請求頭表示 跨源請求 或者 預檢請求 源於哪裡, "Access-Control-Allow-Origin" 請求頭 表示這個頁面允許來自域A 的請求(其值為 * 表示允許任何域的遠端請求)。
像我上面提到的,W3 建議瀏覽器在提交實際跨源HTTP 請求前,實現“預檢請求”, 簡而言之,就是一個HTTP OPTIONS 請求:
OPTIONS DomainB.com/foo.aspx HTTP/1.1
如果 foo.aspx 支援 OPTIONS HTTP 指令, 它可能會像下面這樣返回響應:
HTTP/1.1 200 OK
Date: Wed, 01 Mar 2011 15:38:19 GMT
Access-Control-Allow-Origin: http://DomainB.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD
Access-Control-Allow-Headers: X-Requested-With
Access-Control-Max-Age: 1728000
Connection: Keep-Alive
Content-Type: application/json
只有滿足在響應中包含 "Access-Control-Allow-Origin" , 並且其值為 "*" 或者包含提交CORS請求的域,這些強制條件的瀏覽器才能提交正式的跨域請求, 並在 預檢結果快取” 中快取請求結果 。
實現
讓我們看一下伺服器端程式碼,例子如下(ASP.NET和PHP)
ASP.NET (C#)
protected void Page_Load(object sender, EventArgs e) { String data = String.Empty; String returnJSONStr = String.Empty; switch (Request.HttpMethod) { case "GET": data = Request.QueryString["Data"]; returnJSONStr = "{\"Data\":\"Hi remote friend, you tried to passed me data: *" + data + "* through HTTP GET.\"}"; break; case "POST": data = Request.Form["Data"]; returnJSONStr = "{\"Data\":\"Hi remote friend, you tried to POST some mock data: *" + data + "* to me.\"}"; break; case "OPTIONS": break; default: returnBadRequestResponse(); break; } if (String.IsNullOrEmpty(data)) returnBadRequestResponse(); else { Response.AddHeader("Access-Control-Allow-Origin", "*"); Response.ContentType = "application/json"; Response.Write(returnJSONStr); } } private void returnBadRequestResponse() { Response.StatusCode = 400; Response.ContentType = "application/json"; Response.Write("{\"Error\":\"Bad HTTP request type!\"}"); }
PHP
if(isset($["Data"])) { $method=$_SERVER['REQUEST_METHOD']; $data=""; if($method=="POST") { $data=$_POST["Data"]; $fakeData=new FakeData(); $fakeData->Data="Hi remote friend, you tried to POST some mock data: *"+data+"* to me."; $fakeData->Time=new DateTime("now"); } elseif($method=="GET") { $fakeData=new FakeData(); $fakeData->Data="Hi remote friend, you tried to passed me data: *"+data+"* through HTTP GET."; $fakeData->Time=new DateTime("now"); } else { RaiseError(); } header('Content-type: application/json'); $jsonStr= json_encode($fakeData); echo($jsonStr); } else { RaiseError(); } function RaiseError() { http_send_status(405); header("Status: 405 Method Not Allowed"); } /*Classes definition*/ class FakeData { public $Data; public $Time; }
客戶端AJAXY發起請求程式碼:
var cor = null; // cor stands for Cross-Origin request if (window.XMLHttpRequest) { cor = new XMLHttpRequest(); } //else if (window.XDomainRequest) { //cor = new XDomainRequest(); //} else { alert("Your browser does not support Cross-Origin request!"); return; } cor.onreadystatechange = function () { if (cor.readyState == 4) { document.getElementById('lbl').innerHTML = cor.responseText; } }; var data = 'Some fake data'; if (method == 'POST') { cor.open('POST', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx', true); cor.withCredential = "true"; cor.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); cor.send('Data=' + data); } else if (method == 'GET') { cor.open('GET', 'http://WayneYe.com/Demo/CORSDemo/CORSDemoServer.aspx?Data=' + data, true); cor.withCredential = "true"; cor.send(null); }
JS程式碼適用於所有主流瀏覽器(IE8+, FF 3.6+, Chrome 8+),我沒有用IE8所採用的XDomainObject,因為 IE8+, FF and Chrome, Safari等瀏覽器支援XMLHTTP請求。而且XDomainObject(XDR)似乎有很多限制(參考: http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx)
結論
跨源資源共享為網站開發人員實現跨源通訊提供了一個安全,靈活,標準的方案。也許是時候擯棄像JSONP,Flash,Silverlight,server bridge以及window.name等等並不是很實用的方法。
參考資料
-
跨源資源共享
http://www.w3.org/TR/cors/
http://dev.w3.org/2006/waf/access-control/ -
HTTP許可權控制
https://developer.mozilla.org/En/HTTP_access_control -
IIS Verbs configuration
http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/verbs
原文地址:http://www.codeproject.com/Articles/185506/AJAX-Cross-Origin-HTTP-request
相關文章
- ajax跨域請求之CORS的使用跨域CORS
- Ajax+SpringMVC實現跨域請求SpringMVC跨域
- [Http] 跨站請求偽造(CSRF)HTTP
- csrf解決Ajax請求跨站問題
- Ajax 跨域請求 Access to XMLHttpRequest 解決方案跨域XMLHTTP
- js ajax請求封裝及解決node請求跨域問題JS封裝跨域
- ajax請求
- Python全棧Web(AjaxJQuery-AJAX跨域請求)Python全棧WebjQuery跨域
- ajax請求 juery
- 前端http請求跨域問題解決前端HTTP跨域
- 跨域請求之jQuery的ajax jsonp的使用解惑跨域jQueryJSON
- Koa2框架利用CORS完成跨域ajax請求框架CORS跨域
- Flutter Http請求開源庫-dioFlutterHTTP
- AJAX 跨域請求解跨域
- 原生ajax請求&JSONPJSON
- 通過http上下文判斷是否是Ajax請求HTTP
- http 請求-01-AJAX(Asynchronous JavaScript and XML)入門介紹, ajax 的優缺點HTTPJavaScriptXML
- 巧用javascript ajax,實現跨域請求外帶,增大漏洞危害JavaScript跨域
- 跨域請求cookie資源共享詳解跨域Cookie
- 中止請求和超時 跨域的HTTP請求 認證方式 JSONP跨域HTTPJSON
- 跨域請求跨域
- springmvc處理ajax請求SpringMVC
- 封裝ajax、axios請求封裝iOS
- go http請求GoHTTP
- http請求概述HTTP
- Jsoup http請求JSHTTP
- 跨域是什麼?跨域請求資源有哪些方法?跨域
- CORS跨域請求CORS跨域
- vue跨域請求Vue跨域
- 合併HTTP請求vs並行HTTP請求,到底誰更快?HTTP並行
- 合併HTTP請求 vs 並行HTTP請求,到底誰更快?HTTP並行
- ajax請求servlet返回資料Servlet
- KKB : Jquery實現Ajax請求jQuery
- 如何傳送請求以及AJAX
- Ajax請求後臺資料
- 解決 jquery使用ajax請求發生跨域問題的辦法jQuery跨域
- ajax中POST請求與引數(請求體)設定
- ajax中設定請求頭和自定義請求頭
- HTTP請求報文HTTP