【重要 前端】在這個大談前後分離的時代,如何去解決vue2 前後端分離項目ajax跨域問題?

在這個大談 前後分離 的時代,我相信有很多是後台轉前端,也有很多是前端小白做起的同學。前後分離雖美好,但也是布滿了大大小小的坑,所以今天,和大家一起來探討一下,如何實現前後分離的同時,可以實現跨域通信,甚至跨域登錄信息共享,希望各位踴躍交流。


利用CORS標準解決vue2 前後端分離項目ajax跨域問題

前言

其實在這個大談前後分離的時代,我相信有很多是後台轉前端,也有很多是前端小白做起的同學。前後分離雖美好,但也是布滿了大大小小的坑,所以今天,跟大家手摸手的,一起去了解和實現如何前後分離的同時,可以實現跨域通信,甚至跨域登錄信息共享。解決跨域的方案其實一籮筐,網上至少7種以上,今天想給大家聊聊的是比較新,並且前端代碼依然可以優雅開發的CORS解決方案。分享途中可能會有一部分的PHP後台代碼,所以最好帶上你的後台小夥伴一起手拉手觀看。

在聊這個問題前,先要弄清楚為什麼要跨域?

  1. 只是單純為了線下開發,發布之後還是會放在同一個伺服器(推薦用解決方案1)
  2. 發布之後依然是前後台分開部署在不同域名下,支持較新的瀏覽器(IE10+),不涉及cookie和session的使用(推薦使用解決方案2)
  3. 發布之後依然是前後台分開部署在不同域名下,並且希望可以繼續利用後台的seesion來控制登錄會話(推薦用解決方案3)

解決方案1

針對場景1,只是單純為了便於開發,其實只需要在chrome或QQ瀏覽器安裝一個插件Allow-Control-Allow-Origin: *就可以了。可以暫時移除瀏覽器的同源限制策略。等開發完成,把前後台都一起發布到同一域名下即可。

解決方案2

針對場景2,核心是使用跨域資源共享 CORS這個標準來實現。這個解決方案主要後台方面實現,下面以PHP代碼為例

//核心代碼就一句話
//後台輸出響應頭,允許任意或指定域名進行跨域通信
header("Access-Control-Allow-Origin:*");

不過呢,這樣不夠安全,因為任意域名都可以訪問,所以通常我們還需要做點小限制

//第一步:先獲取來訪的域名
if(isset($_SERVER["HTTP_ORIGIN"])!empty($_SERVER["HTTP_ORIGIN"])){//兼容火狐,
safari
$domain = $_SERVER["HTTP_ORIGIN"];
}else if(isset($_SERVER["HTTP_REFERER"])!empty($_SERVER["HTTP_REFERER"])){//兼容
谷歌
$tmp = explode("//",$_SERVER["HTTP_REFERER"]);
$tmp2 = explode("/",$tmp[1]);
$domain = $tmp[0]."//".$tmp2[0].($_SERVER["SERVER_PORT"]!=
"80"?":".$_SERVER["SERVER_PORT"]:"");//獲取來訪域名,例如http://www.bluej.cn:8080
}
//第二步:根據白名單,檢測是否安全域名
function check_safe_domain($domain){
//這裡的白名單,可以在生產環境中保存到資料庫,方便管理
$white_doamin_list = ["http://www.bluej.cn","http://h5.bluej.cn"];
return in_array($domain,$white_doamin_list);
}
//第三步:判斷成功的話,則輸出CORS響應頭
if(check_safe_domain($domain)){
//允許的請求域名,注意一定要先輸出這個允許頭
header("Access-Control-Allow-Origin:".$domain);//輸出對應的安全域名
//允許的請求頭屬性,多個的話,用逗號隔開
header("Access-Control-Allow-Headers:access_token");
//允許的請求方法,多個的話,用逗號隔開
header("Access-Control-Allow-Methods:GET, HEAD, POST, PUT,DELETE, TRACE,
OPTIONS, PATCH");
//預檢請求的有效期,單位是秒(後續的CORS複雜請求,就不需要做預檢測,提升響應速度)
header("Access-Control-Max-Age:86400");
}

這樣便可以進行跨域請求

這裡科普一下,CORS標準規定了,服務端需要發出響應頭信息Access-Control-Allow-Credentials:true,同時還需要客戶端的XMLHttpRequest的請求設置xhr.withCredentials = true,所以必須要兩個巴掌拍起才會響!

關於數據的通信,除了可以在請求體做數據通信外,也可以通過請求頭做數據通信

axios({
method: "post",
url: "http://xxx",
headers:{
"access_token":"61512ac406f4fcd88b1b4cc20b65becd" //可以自定義請求頭
}
...
})

細心的同學會發現上面的後台代碼中,有一句

header("Access-Control-Allow-Headers:access_token");

這一句就是表示允許在請求頭中自定義什麼屬性

解決方案3

在後台代碼中最後的響應頭輸出位置,加多一句話,表示服務端允許攜帶cookie等信息憑證header("Access-Control-Allow-Credentials:true");

//第一步:先獲取來訪的域名
if(isset($_SERVER["HTTP_ORIGIN"])!empty($_SERVER["HTTP_ORIGIN"])){//兼容火狐,
safari
$domain = $_SERVER["HTTP_ORIGIN"];
}else if(isset($_SERVER["HTTP_REFERER"])!empty($_SERVER["HTTP_REFERER"])){//兼容
谷歌
$tmp = explode("//",$_SERVER["HTTP_REFERER"]);
$tmp2 = explode("/",$tmp[1]);
$domain = $tmp[0]."//".$tmp2[0].($_SERVER["SERVER_PORT"]!=
"80"?":".$_SERVER["SERVER_PORT"]:"");//獲取來訪域名,例如http://www.bluej.cn:8080
}
//第二步:根據白名單,檢測是否安全域名
function check_safe_domain($domain){
//這裡的白名單,可以在生產環境中保存到資料庫,方便管理
$white_doamin_list = ["http://www.bluej.cn","http://h5.bluej.cn"];
return in_array($domain,$white_doamin_list);
}
//第三步:判斷成功的話,則輸出CORS響應頭
if(check_safe_domain($domain)){
//允許的請求域名,注意一定要先輸出這個允許頭
header("Access-Control-Allow-Origin:".$domain);//輸出對應的安全域名
//允許的請求頭屬性,多個的話,用逗號隔開
header("Access-Control-Allow-Headers:content-type,uid,access_token");
//允許的請求方法,多個的話,用逗號隔開
header("Access-Control-Allow-Methods:GET, HEAD, POST, PUT,DELETE, TRACE,
OPTIONS, PATCH");
//預檢請求的有效期,單位是秒(後續的CORS複雜請求,就不需要做預檢測,提升響應速度)
header("Access-Control-Max-Age:86400");
//重點就是加上這一句!!! 表示允許帶上cookie等憑證
header("Access-Control-Allow-Credentials:true");
}

同時在客戶端JS文件這邊稍微設置一下即可,這裡以axios這個XMLHttpRequest庫為例,其它庫類似

axios.defaults.withCredentials=true;//可以全局設置,表示攜帶cookie等憑證信息

剩下的就正常請求就可以了

多說兩句

  1. 如果你是兼容PC和手機端,建議使用解決方案2,每次的請求帶上token令牌信息,便可以支持跨域登錄。
  2. 而如果你單純兼容PC端,更建議使用解決方案3,因為這種的話,後端可以用熟悉的session來管理登錄憑證。(因為手機端,尤其是萬惡的微信環境下,session_id還是會有幾率丟失的,不要問我為什麼會知道,哎都是淚)
  3. 本次分享的代碼,主要是為了講清楚原理實現,如果要放到生產環境,請根據具體業務場景進行調整。


首先,前後端分離,開發時的跨域通信問題不可避免。這裡首先要了解跨域的時候瀏覽器會發送2個請求,一個option預檢,一個真正的請求(建議使用谷歌瀏覽器調試,qq瀏覽器會過濾掉option請求).所以在跨域通信的時候通過token或者其他的方式過濾掉option請求就可以了,當然後端也要放開請求頭


之前項目破解京東商家後台,發現他們有個習慣,請求靠 jsonp 方式,可以跨域。好處是,人家大公司,不同項目組一起玩。另外,微信提供的介面,通過 json 傳遞,get或post請求,不知道如何實現,有空我也去百度下。^o^


我當然不會告訴你有解決跨域問題的包啦,像vue、react這樣的框架所用的ajax調用的包,肯定也有相關解決跨域的方法,自己上網搜吧,就不發鏈接了


推薦閱讀:

國內有哪些公司在用Vue.js,有什麼心得?
如何循序漸進的學習Vue.js,有沒有靠譜的學習資料推薦?
提問學了Angular還有學習react或者vue了么?

TAG:前端開發 | Ajax | 跨域 | Vuejs |