Ajax 跨域解決方案

在解決Ajax跨域問題之前简珠,我們先一起來看看什么是瀏覽器的同源策略

一.瀏覽器同源政策

  • 含義
    1995年,同源政策由 Netscape 公司引入瀏覽器哀峻。目前呈枉,所有瀏覽器都實(shí)行這個(gè)政策。
    最初堪侯,它的含義是指嚎尤,A網(wǎng)頁設(shè)置的 Cookie,B網(wǎng)頁不能打開伍宦,除非這兩個(gè)網(wǎng)頁"同源"芽死。所謂"同源"指的是"三個(gè)相同"。

協(xié)議相同
域名相同
端口相同

舉例來說次洼,http://www.guangzhou.com/p/page.html這個(gè)網(wǎng)址关贵,協(xié)議是http://,域名是 www.guangzhou.com卖毁,端口是80(默認(rèn)端口可以省略)揖曾。它的同源情況如下。

http://www.guangzhou.com/dir/other.html: 同源
http://guangzhou.com/dir/other.html: 不同源(域名不同)
http://v2.www.guangzhou.com/dir/other.html:不同源(域名不同)
http://www.guangzhou.com:81/dir/other.html:不同源(端口不同)

  • 目的
    同源政策的目的亥啦,是為了保證用戶信息的安全炭剪,防止惡意的網(wǎng)站竊取數(shù)據(jù)。
    設(shè)想這樣一種情況:A網(wǎng)站是一家銀行翔脱,用戶登錄以后奴拦,又去瀏覽其他網(wǎng)站。如果其他網(wǎng)站可以讀取A網(wǎng)站的 Cookie届吁,會(huì)發(fā)生什么错妖?
    很顯然,如果 Cookie 包含隱私(比如存款總額)疚沐,這些信息就會(huì)泄漏暂氯。更可怕的是,Cookie 往往用來保存用戶的登錄狀態(tài)亮蛔,如果用戶沒有退出登錄痴施,其他網(wǎng)站就可以冒充用戶,為所欲為尔邓。因?yàn)闉g覽器同時(shí)還規(guī)定晾剖,提交表單不受同源政策的限制锉矢。
    由此可見梯嗽,"同源政策"是必需的,否則 Cookie 可以共享沽损,互聯(lián)網(wǎng)就毫無安全可言了灯节。

  • 限制范圍
    隨著互聯(lián)網(wǎng)的發(fā)展,"同源政策"越來越嚴(yán)格。目前炎疆,如果非同源卡骂,共有三種行為受到限制。

(1) Cookie形入、LocalStorage 和 IndexDB 無法讀取全跨。
(2) DOM 無法獲得。
(3) AJAX 請(qǐng)求不能發(fā)送亿遂。

雖然這些限制是必要的浓若,但是有時(shí)很不方便,合理的用途也受到影響蛇数。

在同源策略下挪钓,在某個(gè)服務(wù)器下的頁面是無法獲取到該服務(wù)器以外的數(shù)據(jù)的,但img耳舅、iframe碌上、script等標(biāo)簽是個(gè)例外,這些標(biāo)簽可以通過src屬性請(qǐng)求到其他服務(wù)器上的數(shù)據(jù)浦徊。

我們下面只專注于ajax跨域請(qǐng)求A笥琛!盔性!


二.如何解決ajax跨域

一般ajax跨域就是通過代理吗蚌,JSONP或者CORS三種方式解決(注意臀突,現(xiàn)在JSONP很少用了扮宠,所以了解下即可)

(1)JSONP方式解決跨域問題

jsonp解決跨域問題是一個(gè)比較古老的方案(實(shí)際中不推薦使用),這里做簡(jiǎn)單介紹(實(shí)際項(xiàng)目中如果要使用JSONP,一般會(huì)使用JQ等對(duì)JSONP進(jìn)行了封裝的類庫來進(jìn)行ajax請(qǐng)求)
實(shí)現(xiàn)原理
JSONP之所以能夠用來解決跨域方案,主要是因?yàn)?<script> 腳本擁有跨域能力,而JSONP正是利用這一點(diǎn)來實(shí)現(xiàn)。具體原理如圖

jsonp-theory.png

實(shí)現(xiàn)流程
客戶端網(wǎng)頁網(wǎng)頁通過添加一個(gè)<script>元素涛舍,向服務(wù)器請(qǐng)求JSON數(shù)據(jù)暂筝,這種做法不受同源政策限制
當(dāng)我們通過JSONP模式請(qǐng)求跨域資源時(shí)箩言,服務(wù)器返回給客戶端一段javascript代碼,這段javascript代碼自動(dòng)調(diào)用客戶端回調(diào)函數(shù)焕襟。
客戶端

jsonp.png

服務(wù)器端(PHP)

<?php
$staff = array
    (
        array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "總經(jīng)理"),
        array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "開發(fā)工程師"),
        array("name" => "黃蓉", "number" => "103", "sex" => "女", "job" => "產(chǎn)品經(jīng)理")
    );

$jsonp = $_GET["callback"];
    //檢查是否有員工編號(hào)的參數(shù)
if (!isset($_GET["number"]) || empty($_GET["number"])) {
        echo $jsonp . '({"success":false,"msg":"參數(shù)錯(cuò)誤"})';
        return;
    }

//獲取number參數(shù)
$number = $_GET["number"];
$result = $jsonp . '({"success":false,"msg":"沒有找到員工陨收。"})';
    
//遍歷$staff多維數(shù)組,查找key值為number的員工是否存在鸵赖,如果存在务漩,則修改返回結(jié)果
foreach ($staff as $value) {
    if ($value["number"] == $number) {
        $result = $jsonp . '({"success":true,"msg":"找到員工:?jiǎn)T工編號(hào):' . $value["number"] .
                            ',員工姓名:' . $value["name"] . 
                            '它褪,員工性別:' . $value["sex"] . 
                            '饵骨,員工職位:' . $value["job"] . '"})';
        break;
        }
    }
 echo $result;

由于<script>元素請(qǐng)求的腳本,直接作為代碼運(yùn)行茫打。這時(shí)居触,只要瀏覽器定義了foo函數(shù)妖混,該函數(shù)就會(huì)立即調(diào)用。作為參數(shù)的JSON數(shù)據(jù)被視為JavaScript對(duì)象轮洋,而不是字符串制市,因此避免了使用JSON.parse的步驟。
注意,一般的JSONP接口和普通接口返回?cái)?shù)據(jù)是有區(qū)別的,所以接口如果要做JSONO兼容,需要進(jìn)行判斷是否有對(duì)應(yīng)callback關(guān)鍵字參數(shù),如果有則是JSONP請(qǐng)求,返回JSONP數(shù)據(jù),否則返回普通數(shù)據(jù)

附(JQ對(duì)JSONP進(jìn)行了封裝的類庫來進(jìn)行ajax請(qǐng)求):


Jq-Jsonp.png

使用注意
基于JSONP的實(shí)現(xiàn)原理,所以JSONP只能是“GET”請(qǐng)求,不能進(jìn)行較為復(fù)雜的POST和其它請(qǐng)求,所以遇到那種情況,就得參考下面的CORS解決跨域了(所以如今它也基本被淘汰了)

(2)CORS解決跨域問題

CORS請(qǐng)求原理
CORS是一個(gè)W3C標(biāo)準(zhǔn)弊予,全稱是"跨域資源共享"(Cross-origin resource sharing)祥楣。它允許瀏覽器向跨源服務(wù)器,發(fā)出XMLHttpRequest請(qǐng)求汉柒,從而克服了AJAX只能同源使用的限制荣堰。

基本上目前所有的瀏覽器都實(shí)現(xiàn)了CORS標(biāo)準(zhǔn)(IE10以下不支持),其實(shí)目前幾乎所有的瀏覽器ajax請(qǐng)求都是基于CORS機(jī)制的,只不過可能平時(shí)前端開發(fā)人員并不關(guān)心而已(所以說其實(shí)現(xiàn)在CORS解決方案主要是考慮后臺(tái)該如何實(shí)現(xiàn)的問題)。

cors-theory.png

如何判斷是否是簡(jiǎn)單請(qǐng)求?

瀏覽器將CORS請(qǐng)求分成兩類:簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)竭翠。只要同時(shí)滿足以下兩大條件振坚,就屬于簡(jiǎn)單請(qǐng)求。

  • 請(qǐng)求方法是以下三種方法之一:HEAD,GET,POST

  • HTTP的頭信息不超出以下幾種字段:

Accept

Accept-Language

Content-Language

Last-Event-ID

Content-Type(只限于三個(gè)值application/x-www-form-urlencoded斋扰、 multipart/form-data渡八、text/plain)

凡是不同時(shí)滿足上面兩個(gè)條件,就屬于非簡(jiǎn)單請(qǐng)求传货。

實(shí)現(xiàn)——PHP后臺(tái)配置
PHP后臺(tái)的配置幾乎是所有后臺(tái)中最為簡(jiǎn)單的,遵循如下步驟即可:
第一步:配置Php 后臺(tái)允許跨域

<?php
header('Access-Control-Allow-Origin:*');  //支持全域名訪問屎鳍,不安全,部署后需要固定限制為客戶端網(wǎng)址
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE'); //支持的http 動(dòng)作
header('Access-Control-Allow-Headers:x-requested-with,content-type');  //響應(yīng)頭 請(qǐng)按照自己需求添加问裕。

實(shí)現(xiàn)——Node.js后臺(tái)配置(express框架)

app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
        //這段僅僅為了方便返回json而已
    res.header("Content-Type", "application/json;charset=utf-8");
    if(req.method == 'OPTIONS') {
        //讓options請(qǐng)求快速返回
        res.sendStatus(200); 
    } else { 
        next(); 
    }
});
(3)代理請(qǐng)求方式解決跨域問題

這種方式是通過后臺(tái)(ASP逮壁、PHP、JAVA粮宛、ASP.NET)獲取其他域名下的內(nèi)容窥淆,然后再把獲得內(nèi)容返回到前端,這樣因?yàn)樵谕粋€(gè)域名下巍杈,所以就不會(huì)出現(xiàn)跨域的問題忧饭。

實(shí)現(xiàn)過程
比如在廣州的WEB服務(wù)器的后臺(tái)(www.guangzhou.com/proxy-shanghaiservice.php)來調(diào)用上海(www.shanghai.com/service.php)的服務(wù),然后把響應(yīng)的結(jié)果返回前端筷畦,這樣前端調(diào)用廣州同域名的服務(wù)就和調(diào)用上海的服務(wù)效果相同了词裤。


參考資料:
瀏覽器同源政策及其規(guī)避方法

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鳖宾,隨后出現(xiàn)的幾起案子吼砂,更是在濱河造成了極大的恐慌,老刑警劉巖鼎文,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件渔肩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡漂问,警方通過查閱死者的電腦和手機(jī)赖瞒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚤假,“玉大人栏饮,你說我怎么就攤上這事×籽觯” “怎么了袍嬉?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)灶平。 經(jīng)常有香客問我伺通,道長(zhǎng),這世上最難降的妖魔是什么逢享? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任罐监,我火速辦了婚禮,結(jié)果婚禮上瞒爬,老公的妹妹穿的比我還像新娘弓柱。我一直安慰自己,他們只是感情好侧但,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布矢空。 她就那樣靜靜地躺著,像睡著了一般禀横。 火紅的嫁衣襯著肌膚如雪屁药。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天柏锄,我揣著相機(jī)與錄音酿箭,去河邊找鬼。 笑死趾娃,一個(gè)胖子當(dāng)著我的面吹牛七问,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茫舶,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼械巡,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了饶氏?” 一聲冷哼從身側(cè)響起讥耗,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疹启,沒想到半個(gè)月后古程,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喊崖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年挣磨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了雇逞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茁裙,死狀恐怖塘砸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情晤锥,我是刑警寧澤掉蔬,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站矾瘾,受9級(jí)特大地震影響女轿,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜壕翩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一蛉迹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧放妈,春花似錦婿禽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至挽绩,卻和暖如春膛壹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唉堪。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工模聋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人唠亚。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓链方,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親灶搜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子祟蚀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容