標籤:目錄 apn exp oql false rc4 gid doc mtd
緣起
由於瀏覽器的同源策略,非同源不可請求。
但是,在實踐當中,經常會出現需要跨域請求資源的情況,比較典型的例如某個子網域名稱向負責進行使用者驗證的子網域名稱請求使用者資訊等應用。
以前要實現跨域訪問,可以通過JSONP、Flash或者伺服器中轉的方式來實現,但是現在我們有了CORS。
CORS與JSONP相比,無疑更為先進、方便和可靠。
| 123 |
1、 JSONP只能實現GET請求,而CORS支援所有類型的HTTP請求。2、 使用CORS,開發人員可以使用普通的XMLHttpRequest發起請求和獲得資料,比起JSONP有更好的錯誤處理。3、 JSONP主要被老的瀏覽器支援,它們往往不支援CORS,而絕大多數現代瀏覽器都已經支援了CORS |
JSONP的最基本的原理
動態添加一個<script>標籤,而script標籤的src屬性是沒有跨域的限制的。這樣說來,這種跨域方式其實與ajax XmlHttpRequest協議無關了。
這樣其實"jQuery AJAX跨域問題"就成了個偽命題,jquery $.ajax方法名有誤導人之嫌。
如果設為dataType: ‘jsonp‘,這個$.ajax方法就和ajax XmlHttpRequest沒什麼關係了,取而代之的則是JSONP協議。JSONP是一個非官方的協議,它允許在伺服器端整合Script tags返回至用戶端,通過javascript callback的形式實現跨域訪問。
JSONP即JSON with Padding。由於同源策略的限制,XmlHttpRequest只允許請求當前源(網域名稱、協議、連接埠)的資源。如果要進行跨域請求, 我們可以通過使用html的script標記來進行跨域請求,並在響應中返回要執行的script代碼,其中可以直接使用JSON傳遞 javascript對象。 這種跨域的通訊方式稱為JSONP。
Cross-Origin Resource sharing
這是W3C 新出的一個標準,簡單的講就是通過伺服器/用戶端 一些Headers的設定及確認 來實現跨域請求,這些包頭有
Cross-Origin Resource sharing
這是W3C 新出的一個標準,簡單的講就是通過伺服器/用戶端 一些Headers的設定及確認 來實現跨域請求,這些包頭有
Syntax
- 5.1
Access-Control-Allow-Origin Response Header
- 5.2
Access-Control-Allow-Credentials Response Header
- 5.3
Access-Control-Expose-Headers Response Header
- 5.4
Access-Control-Max-Age Response Header
- 5.5
Access-Control-Allow-Methods Response Header
- 5.6
Access-Control-Allow-Headers Response Header
- 5.7
Origin Request Header
- 5.8
Access-Control-Request-Method Request Header
- 5.9
Access-Control-Request-Headers Request Header
例如
- Access-Control-Allow-Origin: http://www.test.com
- Access-Control-Allow-Methods: POST, GET, OPTIONS
- Access-Control-Allow-Headers: POWERED-BY-MENGXIANHUI
- Access-Control-Max-Age: 30
可以參考 http://www.w3.org/TR/access-control/#access-control-allow-origin-response-header w3c的網站
| 1234567 |
Access-Control-Allow-Origin: 允許跨域訪問的域,可以是一個域的列表,也可以是萬用字元"*"。這裡要注意Origin規則只對網域名稱有效,並不會對子目錄有效。即http://www.test/test/是無效的。但是不同子網域名稱需要分開設定,這裡的規則可以參照那篇同源策略Access-Control-Allow-Credentials: 是否允許請求帶有驗證資訊,這部分將會在下面詳細解釋Access-Control-Expose-Headers: 允許指令碼訪問的返回頭,請求成功後,指令碼可以在XMLHttpRequest中訪問這些頭的資訊(貌似webkit沒有實現這個)Access-Control-Max-Age: 緩衝此次請求的秒數。在這個時間範圍內,所有同類型的請求都將不再發送預檢請求而是直接使用此次返回的頭作為判斷依據,非常有用,大幅最佳化請求次數Access-Control-Allow-Methods: 允許使用的要求方法,以逗號隔開Access-Control-Allow-Headers: 允許自訂的頭部,以逗號隔開,大小寫不敏感 |
Access-Control-Allow-Credentials
在跨域請求中,預設情況下,HTTP Authentication資訊,Cookie頭以及使用者的SSL認證無論在預檢請求中或是在實際請求都是不會被發送的。
但是,通過設定XMLHttpRequest的credentials為true,就會啟用認證資訊機制。
雖然簡單請求還是不需要發送預檢請求,但是此時判斷請求是否成功需要額外判斷Access-Control-Allow-Credentials,如果Access-Control-Allow-Credentials為false,請求失敗。
十分需要注意的的一點就是此時Access-Control-Allow-Origin不能為萬用字元"*"(真是便宜了一幫偷懶的程式員),如果Access-Control-Allow-Origin是萬用字元"*"的話,仍將認為請求失敗
即便是失敗的請求,如果返回頭中有Set-Cookie的頭,瀏覽器還是會照常設定Cookie
用戶端頁面test.php
| 1234567891011121314151617181920212223242526272829 |
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>crossDomainRequest</title> </head> <body> <input type=‘button‘ value=‘開始測試‘ onclick="crossDomainRequest()" /> <div id="content"></div> <script type="text/javascript">function createXHR(){ return window.XMLHttpRequest? new XMLHttpRequest(): new ActiveXObject("Microsoft.XMLHTTP");}function getappkey(url){ xmlHttp = createXHR(); xmlHttp.open("GET",url,false); xmlHttp.send(); result = xmlHttp.responseText; return result;}function crossDomainRequest(){ var content =getappkey(‘http://127.0.0.10/gettest.php‘); document.getElementById("content").innerHTML=content;}</script> </body> </html> |
服務端頁面gettest.php
| 1234 |
<?phpheader("Access-Control-Allow-Origin: http://127.0.0.1");echo "test success!";?> |
如果不允許的話
所以跨域有以下幾種方法
通過webserver【nginx】配置來跨域
| 123456789101112131415161718192021222324252627282930313233343536 |
## Wide-open CORS config for nginx#location / { if ($request_method = ‘OPTIONS‘) { add_header ‘Access-Control-Allow-Origin‘ ‘*‘; # # Om nom nom cookies # add_header ‘Access-Control-Allow-Credentials‘ ‘true‘; add_header ‘Access-Control-Allow-Methods‘ ‘GET, POST, OPTIONS‘; # # Custom headers and headers various browsers *should* be OK with but aren‘t # add_header ‘Access-Control-Allow-Headers‘ ‘DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type‘; # # Tell client that this pre-flight info is valid for 20 days # add_header ‘Access-Control-Max-Age‘ 1728000; add_header ‘Content-Type‘ ‘text/plain charset=UTF-8‘; add_header ‘Content-Length‘ 0; return 204; } if ($request_method = ‘POST‘) { add_header ‘Access-Control-Allow-Origin‘ ‘*‘; add_header ‘Access-Control-Allow-Credentials‘ ‘true‘; add_header ‘Access-Control-Allow-Methods‘ ‘GET, POST, OPTIONS‘; add_header ‘Access-Control-Allow-Headers‘ ‘DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type‘; } if ($request_method = ‘GET‘) { add_header ‘Access-Control-Allow-Origin‘ ‘*‘; add_header ‘Access-Control-Allow-Credentials‘ ‘true‘; add_header ‘Access-Control-Allow-Methods‘ ‘GET, POST, OPTIONS‘; add_header ‘Access-Control-Allow-Headers‘ ‘DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type‘; }} |
範例程式碼
| 12345678910 |
location /{add_header ‘Access-Control-Allow-Origin‘ ‘http://www.test.com‘;add_header ‘Access-Control-Allow-Credentials‘ ‘true‘;add_header ‘Access-Control-Allow-Methods‘ ‘GET‘; ...... } |
第一條指令:授權從http://www.test.com的請求
第二條指令:當該標誌為真時,響應於該請求是否可以被暴露
第三天指令:指定請求的方法,可以是GET,POST等
如果需要允許來自任何域的訪問,可以這樣配置
| 1 |
Access-Control-Allow-Origin: * |
通過後端程式來跨域
| 1234567891011121314151617181920 |
<?phpheader("Access-Control-Allow-Origin:http://www.test.com");header("Access-Control-Allow-Origin:*");echo json_encode($_POST);?><script type="text/javascript">$("#ajax").click(function(){ $.ajax({ type: "POST", url: "http://www.test.com/test2.php", data: ‘name=test‘, dataType:"json", success: function(data){ $(‘#Result‘).text(data.name); } });});</script> |
JSONP
| 1234567891011121314151617181920212223242526 |
<?phpif(isset($_GET[‘name‘]) && isset($_GET[‘callback‘])) //callback根js端要對應,不然會報錯的{ echo $_GET[‘callback‘]. ‘(‘ . json_encode($_GET) . ‘);}?><script type="text/javascript">$("#jsonp").click(function(){ $.ajax({ url: ‘http://www.test.com/test1.php‘, data: {name: ‘jsonp‘}, dataType: ‘jsonp‘, jsonp: ‘callback‘, //為服務端準備的參數 jsonpCallback: ‘getdata‘, //回呼函數 success: function(){ alert("success"); } });});function getdata(data){ $(‘#Result‘).text(data.name);}</script> |
getJSON
| 12345678 |
<script type="text/javascript">$("#getjson").click(function(){ $.getJSON(‘http://www.test.com/test1.php?name=getjson&callback=?‘, function(data){ //沒有回呼函數,直接處理 $(‘#Result‘).text(data.name); })})</script> |
getScript
| 12345 |
<script type="text/javascript">$("#getscript").click(function(){ $.getScript(‘http://www.test.com/test1.php?name=getscript&callback=getdata‘);});</script> |
XMLHttpRequest的跨域請求