Web 跨域

為什么需要跨域?

瀏覽器 同源策略 (域名、協(xié)議巾兆、端口相同)限制其他網(wǎng)頁對(duì)本網(wǎng)頁進(jìn)行非法篡改, 只有帶有 src 屬性的標(biāo)簽才允許跨域(<img> <iframe> <script>)

JSONP

JSONP(JSON with Padding)是一個(gè)非官方的協(xié)議路鹰,它允許在服務(wù)器端集成Script tags返回至客戶端袜腥,通過javascript callback的形式實(shí)現(xiàn)跨域訪問(這僅僅是JSONP簡單的實(shí)現(xiàn)形式)辛块。利用<script/>標(biāo)簽對(duì)javascript進(jìn)行動(dòng)態(tài)解析來實(shí)現(xiàn)(其實(shí)也可以用eval函數(shù)),然后在服務(wù)端輸出JSON數(shù)據(jù)并執(zhí)行回調(diào)函數(shù)贼邓,從而解決了跨域的數(shù)據(jù)請(qǐng)求。

IE 兼容

P3P 規(guī)范讓 IE 跨域接受第三方 cookie

header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');

原理

1.首先在客戶端注冊(cè)一個(gè)callback, 然后把callback的名字傳給服務(wù)器热鞍。

  1. 此時(shí)葫慎,服務(wù)器先生成 json 數(shù)據(jù)。
  2. 然后以 javascript 語法的方式薇宠,生成一個(gè)function , function 名字就是傳遞上來的參數(shù) jsonp.
  3. 最后將 json 數(shù)據(jù)直接以入?yún)⒌姆绞酵蛋欤胖玫?function 中,這樣就生成了一段 js 語法的文檔澄港,返回給客戶端椒涯。
  4. 客戶端瀏覽器,解析script標(biāo)簽回梧,并執(zhí)行返回的 javascript 文檔废岂,此時(shí)數(shù)據(jù)作為參數(shù),傳入到了客戶端預(yù)先定義好的 callback 函數(shù)里.(動(dòng)態(tài)執(zhí)行回調(diào)函數(shù))

缺點(diǎn)

  • 只能采用GET請(qǐng)求,數(shù)據(jù)量大的跨域請(qǐng)求不能使用
  • 如果使用eval來解析會(huì)容易出現(xiàn)安全問題
  • 沒有JSONP調(diào)用的錯(cuò)誤處理,如果無效靜默失敗
  • 被不信任的服務(wù)使用時(shí)會(huì)很危險(xiǎn),因?yàn)?JSONP 服務(wù)返回打包在函數(shù)調(diào)用中的 JSON 響應(yīng)狱意,而函數(shù)調(diào)用是由瀏覽器執(zhí)行的湖苞,這使宿主 Web 應(yīng)用程序更容易受到各類攻擊。

代碼示例

原生實(shí)現(xiàn)JSONP跨域
// 客戶端
<script>
    // 這是回調(diào)方法
    function cb(data){
        alert(data.website);
    }
</script>
<!--這是跨域請(qǐng)求的代碼,切記详囤,這段代碼要在回調(diào)函數(shù)之后-->
<script src="http://172.22.22.120/new/ajax_jsonp.php?callback=cb"></script>
<!-- 服務(wù)端 -->
<?php
    $cb = htmlspecialchars($_GET['callback']);  // 注意了财骨,這里要做好過濾,防止xss攻擊
    echo $cb,'(',json_encode(array('website'=>'hcoding')),')';  // 返回客戶端的數(shù)據(jù)為:cb({"name":"hcoding"})  這是一段js代碼
?>
AJax實(shí)現(xiàn)JSONP

客戶端代碼:

<script>
    // 客戶端使用getJSON方法請(qǐng)求另一臺(tái)機(jī)子上的腳本
    // 瀏覽器會(huì)生成一個(gè)隨機(jī)的callback參數(shù)
    $.getJSON("http://172.22.22.120/new/ajax_jsonp.php?callback=?",function(json){
        alert(json.website);
    });
// $.getJSON簡單易用,但就是不能指定回調(diào)函數(shù)隆箩。
</script>
//或者
<script>
    $.ajax({
        type : "GET",
        url : "http://172.22.22.120/new/ajax_jsonp.php",
        dataType : "jsonp",    // 數(shù)據(jù)格式指定為jsonp
        jsonp: "callback",     // 服務(wù)點(diǎn)通過這個(gè)鍵值獲取回調(diào)方法
        jsonpCallback:"cb",   // 指定回調(diào)方法
        success : function(json){

        },
    });   

    // 回調(diào)方法
    function cb(data){
        alert(data.website);
    }
</script>

服務(wù)端PHP腳本代碼:

<?php
    $cb = htmlspecialchars($_GET['callback']);  // 注意了滑肉,這里要做好過濾,防止xss攻擊
    echo $cb,'(',json_encode(array('website'=>'hcoding')),')';  // 返回客戶端的數(shù)據(jù),這是一段js代碼
?>

CORS (IE8+) 推薦使用

跨源資源共享標(biāo)準(zhǔn)( cross-origin sharing standard ) 使得以下場景可以使用跨站 HTTP 請(qǐng)求:

  • 如上所述摘仅,使用 XMLHttpRequest 發(fā)起跨站 HTTP 請(qǐng)求靶庙。
  • Web 字體 (CSS 中通過 @font-face 使用跨站字體資源), 因此,網(wǎng)站就可以發(fā)布 TrueType 字體資源娃属,并只允許已授權(quán)網(wǎng)站進(jìn)行跨站調(diào)用六荒。
  • WebGL 貼圖
  • 使用 drawImage API 在 canvas 上畫圖

相關(guān)Header

服務(wù)端設(shè)置Header: (ResponseHeader)

  • Access-Control-Allow-Origin * 服務(wù)器設(shè)置允許訪問的源的網(wǎng)址
  • * 可以為網(wǎng)站網(wǎng)址 如http://api.a.com
  • Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header 允許訪問服務(wù)器的頭信息
  • Access-Control-Max-Age: <delta-seconds> 允許預(yù)請(qǐng)求參數(shù)緩存的毫秒數(shù),在此期間不用發(fā)出另一條預(yù)請(qǐng)求
  • Access-Control-Allow-Credentials: true | false
  • Access-Control-Allow-Methods: <method>[, <method>]*
  • Access-Control-Allow-Headers: X-PINGOTHER 這樣在實(shí)際的請(qǐng)求里請(qǐng)求頭信息里就可以有這么一條:X-PINGOTHER: pingpong可以有多個(gè)自定義HTTP請(qǐng)求頭,用逗號(hào)分隔.
  • Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP 請(qǐng)求頭(RequestHeader)

注意這些請(qǐng)求頭信息都是在請(qǐng)求服務(wù)器的時(shí)候已經(jīng)為你設(shè)置好的,當(dāng)開發(fā)者使用跨域的XMLHttpRequest的時(shí)候,不需要手動(dòng)的設(shè)置這些頭信息.

  • Origin: <origin>注意,不僅僅是跨域請(qǐng)求,普通請(qǐng)求也會(huì)帶有ORIGIN頭信息.
  • Access-Control-Request-Method: <method>
  • Access-Control-Request-Headers
  • Access-Control-Request-Headers: <field-name>[, <field-name>]*

請(qǐng)求方式

  • 簡單請(qǐng)求
  • 只使用 GET, HEAD 或者 POST 請(qǐng)求方法。如果使用 POST 向服務(wù)器端傳送數(shù)據(jù)矾端,則數(shù)據(jù)類型(Content-Type)只能是 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一種掏击。
  • 不會(huì)使用自定義請(qǐng)求頭(類似于 X-Modified 這種)。
  • 預(yù)請(qǐng)求
  • 請(qǐng)求以 GET, HEAD 或者 POST 以外的方法發(fā)起請(qǐng)求秩铆⊙馔ぃ或者,使用 POST殴玛,但請(qǐng)求數(shù)據(jù)為 application/x-www-form-urlencoded, multipart/form-data 或者 text/plain 以外的數(shù)據(jù)類型捅膘。比如說,用 POST 發(fā)送數(shù)據(jù)類型為 application/xml 或者 text/xml 的 XML 數(shù)據(jù)的請(qǐng)求滚粟。
  • 使用自定義請(qǐng)求頭(比如添加諸如 X-PINGOTHER)
  • 附帶憑證信息的請(qǐng)求
  • XMLHttpRequest和訪問控制功能寻仗,最有趣的特性就是,發(fā)送憑證請(qǐng)求(HTTP Cookies和驗(yàn)證信息)的功能凡壤。一般而言署尤,對(duì)于跨站請(qǐng)求,瀏覽器是不會(huì)發(fā)送憑證信息的亚侠。但如果將XMLHttpRequest的一個(gè)特殊標(biāo)志位設(shè)置為true曹体,瀏覽器就將允許該請(qǐng)求的發(fā)送。

安全問題:不要依賴CORS當(dāng)中的權(quán)限制度硝烂,應(yīng)當(dāng)使用更多其它的措施來保障箕别,比如OAuth2

iframe 隱藏表單,進(jìn)行POST提交(非現(xiàn)代瀏覽器)

  • 建立一個(gè)iframe,iframe內(nèi)的JS創(chuàng)建一個(gè)form表單钢坦,并可以將接收到的參數(shù)放入表單中POST提交
  • 將iframe頁面插入到頁面中究孕。

window.name

有三個(gè)頁面:

  • a.com/app.html:應(yīng)用頁面啥酱。
  • a.com/proxy.html:代理文件爹凹,一般是一個(gè)沒有任何內(nèi)容的html文件,需要和應(yīng)用頁面在同一域下镶殷。
  • b.com/data.html:應(yīng)用頁面需要獲取數(shù)據(jù)的頁面禾酱,可稱為數(shù)據(jù)頁面。

實(shí)現(xiàn)起來基本步驟如下:

  • 在應(yīng)用頁面(a.com/app.html)中創(chuàng)建一個(gè)iframe,把其src指向數(shù)據(jù)頁面(b.com/data.html)颤陶。
    數(shù)據(jù)頁面會(huì)把數(shù)據(jù)附加到這個(gè)iframe的window.name上颗管,data.html代碼如下:
<script type="text/javascript">
    window.name = 'I was there!';    // 這里是要傳輸?shù)臄?shù)據(jù),大小一般為2M滓走,IE和firefox下可以大至32M左右
                                     // 數(shù)據(jù)格式可以自定義垦江,如json、字符串
</script>
  • 在應(yīng)用頁面(a.com/app.html)中監(jiān)聽iframe的onload事件搅方,在此事件中設(shè)置這個(gè)iframe的src指向本地域的代理文件(代理文件和應(yīng)用頁面在同一域下比吭,所以可以相互通信)。app.html部分代碼如下:
<script type="text/javascript">
    var state = 0, 
    iframe = document.createElement('iframe'),
    loadfn = function() {
        if (state === 1) {
            var data = iframe.contentWindow.name;    // 讀取數(shù)據(jù)
            alert(data);    //彈出'I was there!'
        } else if (state === 0) {
            state = 1;
            iframe.contentWindow.location = "http://a.com/proxy.html";    // 設(shè)置的代理文件
        }  
    };
    iframe.src = 'http://b.com/data.html';
    if (iframe.attachEvent) {
        iframe.attachEvent('onload', loadfn);
    } else {
        iframe.onload  = loadfn;
    }
    document.body.appendChild(iframe);
</script>
  • 獲取數(shù)據(jù)以后銷毀這個(gè)iframe姨涡,釋放內(nèi)存衩藤;這也保證了安全(不被其他域frame js訪問)。
<script type="text/javascript">
    iframe.contentWindow.document.write('');
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
</script>

總結(jié)起來即:iframe的src屬性由外域轉(zhuǎn)向本地域涛漂,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域赏表。這個(gè)就巧妙地繞過了瀏覽器的跨域訪問限制,但同時(shí)它又是安全操作匈仗。

HTML5 postMessage(IE8+)

otherWindow.postMessage(message, targetOrigin);

  • otherWindow: 對(duì)接收信息頁面的window的引用瓢剿。可以是頁面中iframe的contentWindow屬性悠轩;window.open的返回值跋选;通過name或下標(biāo)從window.frames取到的值。
  • message: 所要發(fā)送的數(shù)據(jù)哗蜈,string類型前标。
  • targetOrigin: 用于限制otherWindow,“*”表示不作限制

a.com/index.html中的代碼:

<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 若寫成'http://b.com/c/proxy.html'效果一樣
                                        // 若寫成'http://c.com'就不會(huì)執(zhí)行postMessage了
    ifr.contentWindow.postMessage('I was there!', targetOrigin);
};
</script>

b.com/index.html中的代碼:

<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 通過origin屬性判斷消息來源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 彈出"I was there!"
            alert(event.source);  // 對(duì)a.com距潘、index.html中window對(duì)象的引用
                                  // 但由于同源策略炼列,這里event.source不可以訪問window對(duì)象
        }
    }, false);
</script>

用nginx/Apache把B網(wǎng)站的數(shù)據(jù)url反向代理。(數(shù)據(jù)轉(zhuǎn)發(fā))

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末音比,一起剝皮案震驚了整個(gè)濱河市俭尖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洞翩,老刑警劉巖稽犁,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骚亿,居然都是意外死亡已亥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門来屠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘸味,“玉大人,你說我怎么就攤上這事覆醇。” “怎么了传趾?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長泥技。 經(jīng)常有香客問我浆兰,道長,這世上最難降的妖魔是什么珊豹? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任镊讼,我火速辦了婚禮,結(jié)果婚禮上平夜,老公的妹妹穿的比我還像新娘蝶棋。我一直安慰自己,他們只是感情好忽妒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布玩裙。 她就那樣靜靜地躺著,像睡著了一般段直。 火紅的嫁衣襯著肌膚如雪吃溅。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天鸯檬,我揣著相機(jī)與錄音决侈,去河邊找鬼。 笑死喧务,一個(gè)胖子當(dāng)著我的面吹牛赖歌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播功茴,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼庐冯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坎穿?” 一聲冷哼從身側(cè)響起展父,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎玲昧,沒想到半個(gè)月后栖茉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孵延,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年吕漂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隙袁。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡痰娱,死狀恐怖弃榨,靈堂內(nèi)的尸體忽然破棺而出菩收,到底是詐尸還是另有隱情梨睁,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布娜饵,位于F島的核電站坡贺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏箱舞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一晴股、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧电湘,春花似錦隔节、人聲如沸寂呛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽幻妓。三九已至,卻和暖如春劫拢,著一層夾襖步出監(jiān)牢的瞬間肉津,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國打工舱沧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留阀圾,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓狗唉,卻偏偏與公主長得像初烘,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子分俯,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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

  • 同源策略 理解跨域首先必須要了解同源策略肾筐。同源策略是瀏覽器上為安全性考慮實(shí)施的非常重要的安全策略。何謂同源:URL...
    48892085f47c閱讀 721評(píng)論 0 6
  • 昨天測試一個(gè)網(wǎng)頁聯(lián)調(diào)問題時(shí)發(fā)送請(qǐng)求總是發(fā)送不成功缸剪,因?yàn)闀r(shí)IE8環(huán)境提示很有限吗铐,后來無奈開啟IE調(diào)試模式發(fā)現(xiàn)報(bào)錯(cuò)信心...
    Maker在杭州閱讀 1,198評(píng)論 0 5
  • 跨域資源共享 CORS 對(duì)于web開發(fā)來講,由于瀏覽器的同源策略杏节,我們需要經(jīng)常使用一些hack的方法去跨域獲取資源...
    默默先生Alec閱讀 588評(píng)論 0 0
  • 什么是跨域唬渗? 2.) 資源嵌入:典阵、、镊逝、等dom標(biāo)簽壮啊,還有樣式中background:url()、@font-fac...
    電影里的夢i閱讀 2,368評(píng)論 0 5
  • 年末撑蒜,是不是就是各種負(fù)面情緒累積歹啼,然后爆棚。 昨天看了推送座菠,一個(gè)人孤單狸眼,孤獨(dú)都不可怕,可怕的是你跟另一個(gè)人讓你孤獨(dú)...
    淺淺的柒閱讀 402評(píng)論 0 1