ajax跨域問題的三種解決方案

在工作中大家應該都遇到過ajax跨域問題,瀏覽器通常會報:

XMLHttpRequest cannot load http://目標地址No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://當前頁面地址' is therefore not allowed access.


爲什麼會出現這種問題

  • Ajax技術由於受到瀏覽器同源策略的限制,同源策略阻止從一個域上加載的腳本獲取或操作另一個域上的文檔屬性。
  • 所謂同源指的是域名、協議、端口均相等。這意味着,被請求的 URL 的域、協議、端口必須與當前 Web 頁面的保持一致。

    例如:

http://www.abc.com/a/b 調用 http://www.abc.com/d/c(非跨域)

http://www.abc.com/a/b 調用 http://www.def.com/d/c (跨域:域名不一致)

http://www.abc.com:81/a/b 調用 http://www.abc.com:82/d/c (跨域:端口不一致)

http://www.abc.com/a/b 調用 https://www.abc.com/d/c (跨域:協議不同)

請注意:localhost和127.0.0.1雖然都指向本機,但也屬於跨域。

在一個http請求中,http頭部Referer或Origin字段標識了當前域名,Host字段標識了此時請求的域名。

故,如果我們在當前的js頁面,通過ajax請求第三方的數據,就會出現瀏覽器的跨域問題。


解決跨域問題

解決跨域問題,有如下三種方式:

1、使用jsonp

2、服務器代理

3、在服務端設置response header中Access-Control-Allow-Origin字段。

使用jsonp

jsonp解決跨域問題的原理是,瀏覽器的script標籤是不受同源策略限制的,我們可以在script標籤中訪問任何域名下的資源文件。利用這一特性,用script標籤從服務器中請求數據,同時服務器返回一個帶有方法和數據的js代碼,請求完成,調用本地的js方法,來完成數據的處理。

前端實現,以Jquery的ajax方法爲例:

$.ajax({
  type: "get",
  data: "random="+Math.random(),
  url: "https://mp.csdn.net/phpajax/jsonp.php",
  dataType: "jsonp",
  jsonp: "callback",
  success: function(data) {
   console.log(data);
  },
  error: function() {
   console.log('Request Error.');
  }
});

服務端此時返回的不能是普通的json字符串,而是一段可以被前端js執行的一段js代碼。

比較一下json與jsonp格式的區別:

json格式:

{
    "message":"獲取成功",
    "state":"1",
    "result":{"name":"lihua","id":1}
}

jsonp格式:

callback({
    "message":"獲取成功",
    "state":"1",
    "result":{"name":"lihua","id":1}
})
從格式來看,jsonp是在json的基礎上包裝了一個方法名,此方法名是前端請求傳過來的,如請求地址爲:http://localhost/api/user?callback=GetUserInfo,那麼方法名就是GetUserInfo。

php後端服務代碼可以這樣寫(注意輸出返回的格式):

$data = array(
  'rand' => $_GET['random'],
  'msg' => 'Success'
);
echo $_GET['callback'].'('.json_encode($data).')';

jsonp的缺點:

1、JSONP是一種非官方的方法,而且這種方法只支持GET方法,不如POST方法安全。(從實現機制就可明白)。

2、JSONP的實現需要服務器配合,如果是訪問的是第三方的服務器,我們沒有修改服務器的權限,那麼這種方式是不可行的。

服務器代理

這種方式運用的就是服務器的反向代理技術,控制客戶端和服務器的訪問都從代理服務器經過,比如用nginx作爲服務器代理,在nginx上配置客戶端和第三方服務的反向代理,這樣就可保證客戶端、第三方是同源的了,同一個源,都來自代理服務器。

配置Nginx反向代理

編緝Nginx配置文件(/usr/local/nginx/conf/nginx.conf),找到FastCGI段取消註釋並改成合適自己的值:

注意:有時/scripts要改成$document_root,$document_root 代表當前請求在root指令中指定的值。

服務器代理的缺點:

開發比較麻煩,對開發環境比較嚴格,需要在本機上配置代理服務器。

優點:

完美解決使用jsonp,第三方服務沒有修改權限的問題。程序的代碼侵入性小,代碼級別不需要考慮跨域問題。

在服務端設置response header中Access-Control-Allow-Origin字段

1、允許單個域名訪問

指定某域名(http://client.runoob.com)跨域訪問,則只需在http://server.runoob.com/server.php文件頭部添加如下代碼:

header('Access-Control-Allow-Origin:http://client.runoob.com');

2、允許多個域名訪問

指定多個域名(http://client1.runoob.com、http://client2.runoob.com等)跨域訪問,則只需在http://server.runoob.com/server.php文件頭部添加如下代碼:

$origin = isset($_SERVER['HTTP_ORIGIN'])? $_SERVER['HTTP_ORIGIN'] : '';  
  
$allow_origin = array(  
    'http://client1.runoob.com',  
    'http://client2.runoob.com'  
);  
  
if(in_array($origin, $allow_origin)){  
    header('Access-Control-Allow-Origin:'.$origin);       
} 

3、允許所有域名訪問

允許所有域名訪問則只需在http://server.runoob.com/server.php文件頭部添加如下代碼:

header('Access-Control-Allow-Origin:*'); 

缺點:

1、此種解決跨域方案,需要瀏覽器支持H5,因爲這是HTML5解決跨域的方式,如果產品面向的是PC端,這種方式可能就不是一個好的解決方案,如果面向的是手機端,此方法不爲一個簡單、粗暴的好方式。

2、設置*,存在安全隱患。

總結

綜上三種解決跨域的方案,個人感覺使用服務代理最好,沒有破壞瀏覽器的安全策略,但這個對開發環境要高一點。設置response header的方式,根據具體情況分析,要考慮清楚產品面向的用戶。對於jsonp這種方式,雖然沒有破壞瀏覽器的安全策略,但只支持get方式的請求,有點不能接受,因爲get傳輸有參數長度的限制,同時又要考慮傳輸中文的亂碼問題,但如果項目中只是簡單的查詢、展示,這種方式還是可以考慮的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章