前言:隨著前後端分離開發模式的流行,前端調用後臺伺服器介面的時候,如果介面不在同一個域名下,就會產生跨域問題。

  1. 前端域名 test.order.com:8080
  2. 後臺伺服器介面域名 test.leku.com:8080

現象: 在 前臺域名下 使用如下介面進行請求時

$.ajax({
type: "post",
url: "http://test.leku.com:8080/zym/crossDomain",
data: {type: test},
success: function (res) {
console.log(success);
console.log(res);
},
error: function(){
console.log(error);
}
});

雖然介面的返回狀態為 200 ok,但是因為瀏覽器的同源策略會有以下報錯提示:

Failed to load http://test.leku.com:8080/zym/crossDomain: No Access-Control-Allow-Origin header is present on the requested resource. Origin http://test.order.com:8080 is therefore not allowed access.

實際執行的是 error 方法。

問題:

一,為什麼會產生ajax跨域問題?

1,瀏覽器的限制:出於安全考慮,當它發現請求是跨域的時候,會進行一些校驗,如果校驗不通過就會報跨域安全問題,並不是伺服器不允許請求。

2,請求是跨域的:發出去的請求不是本域的,請求的服務、域名、埠任何一個不一樣伺服器就會認為是跨域。

3,也是最重要的原因,因為發出去的是 XHR(XMLHttpRequest)請求,換句話說如果發出去的不是XHR請求,即使跨域 ,瀏覽器也不會報錯。

解決思路:

1,瀏覽器的限制,我們只要讓瀏覽器不再限制,就可以解決跨域的問題。

我們可以通過指定參數,讓瀏覽器不去校驗就可以了,但是這種方法價值不大,因為需要 每個人都做改動,而且是客戶端的改動。

--disable-web-security

2,XHR請求:只要發出去的請求不是XHR請求就可以了,基於這個思路,有以下解決方案

a> jsonp(JSON with Padding):基本原理是動態創建script標籤,遠程調用JSON文件來實現數據傳遞,是一個非官方協議,是一個約定,約定請求的參數中如果包含指定參數(摩默認是callback)這就是一個jsonp請求,伺服器發現是jsonp 請求時候就會把數據由原來的json對象改成js代碼

普通的ajax請求,返回的json對象,而jsonp 請求返回的是js腳本

服務端代碼:

/**
* 跨域測試 服務端介面
* @author zhangyanmin
* @date 2018-04-13
*/
public function crossDomain(){
$data[field1] = a;
$data[field2] = b;
$data[field3] = c;
$data = json_encode($data);
if(isset($_GET[callback])){
echo (.$_GET[callback].(.$data.));
}else{
exit($data);
}
}

客戶端代碼:

$.ajax({
type: "get",
url: "http://test.leku.com:8080/zym/crossDomain",
dataType:jsonp,
data: {callback: jsp_cal},
success: function (res) {
console.log(success);
console.log(res);
},
error: function(){
console.log(error);
}
});
//jsp_cal 為雙方約定的回調函數,默認是 callback
var jsp_cal = function(flag){
console.log(success);
console.log(flag);
}

jsonp 的弊端:

1,需要伺服器端改動代碼支持;

2,只支持get 方法,即使指定post請求也是get方式;

3,發送的不是XHR(新特性支持非同步,各種事件等)請求

3,跨域:

a> 被調用方修改(支持跨域):在響應頭添加指定的欄位,支持跨域請求

1,伺服器端實現

public function addHeader(){
// header(Access-Control-Allow-Origin:*);
// header(Access-Control-Allow-Methods:*);
// 允許 指定域,指定方法跨域請求
header(Access-Control-Allow-Origin:http://test.order.com:8080/);
header(Access-Control-Allow-Methods:GET);
$data[field1] = a;
$data[field2] = b;
$data[field3] = c;
$data = json_encode($data);
exit($data);
}

簡單請求:先執行後判斷,如果發現是跨域請求,會檢測是否允許跨域

非簡單請求:先發送options預檢命令,檢測通過後再發送請求,會請求兩次

發送兩次請求會佔用更多的網路資源,所以就有預檢命令緩存

//將預檢命令緩存一個小時,第一次請求時會發送連個請求,過期之前再請求只發送一次請求
header(Access-Control-Allow-Max-Age:"3600");

帶cookie的跨域請求(發送的是被調用方的cookie)

服務端代碼:
public function getCookie(){
// 帶cookie 時,origin必須完全匹配不能為*
header(Access-Control-Allow-Origin:http://test.order.com:8080);
header(Access-Control-Allow-Methods:*);
// enable cookie
header(Access-Control-Allow-Credentials:true);
}

客戶端代碼:

// 獲取Cookie的請求
$.ajax({
type: "get",
url: "http://test.leku.com:8080/zym/getCookie",
xhrFields:{
withCredentials:true,//發送Ajax時,Request header中便會帶上 Cookie 信息
},
success: function (res) {
console.log(res);
},
error: function(){
console.log(error);
}
});

發送自定義頭的跨域請求

2,修改nginx 配置

var xhr = new XMLHttpRequest();
$.ajax({
type: "get",
url: "http://test.leku.com:8080/zym/getHeader",
headers:{
x-headre1:AAAA,//發送Ajax時,Request header中便會帶上 Cookie 信息
},
beforeSend:function(){
xhr.setRequestHeader(x-headre2,BBBB);
},
success: function (res) {
console.log(res);
},
error: function(){
console.log(error);
}
});
伺服器端代碼
public function getHeader(){
// 帶cookie 時,origin必須完全匹配不能為*
header(Access-Control-Allow-Origin:http://test.order.com:8080);
header(Access-Control-Allow-Methods:*);
//可以在瀏覽器裏複製所有的header信息並返回回去,這樣支持所有的請求頭請求
header(Access-Control-Allow-Headers:Content-Type, x-header1, x-header2);
}

2,修改nginx 配置

server{
………………
location /{
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Allow-Max-Age 3600;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
if ($request_method = OPTIONS){
return 200;
}
}
}

3,apache 配置

<Directory /www/web/yuming.com/public_html/>
Options FollowSymLinks
AllowOverride All
Require all granted
Header set Access-Control-Allow-Origin *
</Directory>

編輯 httpd.conf
找下面這行,把#去掉,目的是開啟apache頭信息自定義模塊
#LoadModule headers_module modules/mod_headers.so

b> 調用方做修改(隱藏跨域,代理)

調用方通過Http反向代理轉發到被調用方的伺服器

server{
listen 80;
servername a.com;
location /{
proxy_pass http://test.order.com:8080;
}
//代理之後的地址
location /ajaxserver{
proxy_pass http://test.leku.com:8080;
}

}

客戶端代碼
$.ajax({
type: "get",
url: "/ajaxserver",//代理後的地址
success: function (res) {
console.log(res);
},
error: function(){
console.log(error);
}
});

來源:慕課網視頻地址


推薦閱讀:
相關文章