跨域

定義

跨域是指從一個(gè)域名的網(wǎng)頁去請(qǐng)求另一個(gè)域名的資源。比如從http://www.baidu.com/ 頁面去請(qǐng)求 http://www.google.com 的資源??缬虻膰?yán)格一點(diǎn)的定義是:只要 協(xié)議,域名,端口有任何一個(gè)的不同,就被當(dāng)作是跨域。

目前學(xué)到的四種跨域的解決方式

JSONP

jsonp是一種跨域通信的手段,它的原理其實(shí)很簡單:
1.首先是利用script標(biāo)簽的src屬性來實(shí)現(xiàn)跨域。
2.通過將前端方法作為參數(shù)傳遞到服務(wù)器端,然后由服務(wù)器端注入?yún)?shù)之后再返回,實(shí)現(xiàn)服務(wù)器端向客戶端通信。
3.由于使用script標(biāo)簽的src屬性,因此只支持get方法。
前端代碼:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>news</title>
    <style type="text/css">
        .container{
            width: 900px;
            maigin: 0 auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <ul class="news">
        </ul>
        <button class="show">show news</button>
    </div>

    <script type="text/javascript">
        $('.show').addEventListener('click',function(){
            var script = document.createElement('script');
            script.src = 'http://b.ji.com:8080/getNews?callback=appendHtml';
            document.body.appendChild(script);
            document.body.removeChild(script);
        })
        function appendHtml(news){
            var html = '';
            for( var i=0; i<news.length; i++){
                html += '<li>' + news[i] + '</li>'
            }
            console.log(html);
            $('.news').innerHTML = html
        }
        function $(id){
            return document.querySelector(id);
        }
    </script>
</body>
</html>

后端代碼:

var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')


http.createServer(function(req,res){
    var pathObj = url.parse(req.url,true)

    switch(pathObj.pathname){
        case '/getNews':
            var news = [
            "第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
            "正直播柴飚/洪煒出戰(zhàn) 男雙力爭會(huì)師決賽",
            "女排將死磕巴西!郎平安排男陪練模仿對(duì)方核心"
            ]
            res.setHeader('content-Type','text/json;charset=utf-8')
            if(pathObj.query.callback){
                res.end(pathObj.query.callback + '('+ JSON.stringify(news) +')')
            }else{
                res.end(JSON.stringify(news))
            }

            break;

            default:
                fs.readFile(path.join(__dirname,pathObj.pathname),function(e,data){
                    if(e){
                        res.writeHead(404,'not found')
                        res.end('<h1>404 Not Found</h1>')
                    }else{
                        res.end(data)
                    }
                })
    }
}).listen(8080)
  • 優(yōu)點(diǎn)
    1.兼容性很好,在古老的瀏覽器也能很好的運(yùn)行
    2.不需要 XMLHttpRequest 或 ActiveX 的支持;并且在請(qǐng)求完畢后可以通過調(diào)用 callback 的方式回傳結(jié)果。
    3.不受同源策略的限制
  • 缺點(diǎn)
    1.只支持 GET 請(qǐng)求而不支持 POST 等其它類型的 HTTP 請(qǐng)求。
    2.只支持跨域HTTP請(qǐng)求這種情況,不能解決不同域的兩個(gè)頁面之間如何進(jìn)行JavaScript調(diào)用的問題。
    3.容易遭受XSS攻擊,因?yàn)槲覀兡玫降氖菍?duì)方接口的數(shù)據(jù)作為js執(zhí)行,如果得到的是一個(gè)很危險(xiǎn)js,獲取了用戶信息和cookies,這時(shí)執(zhí)行了js就會(huì)出現(xiàn)安全問題。

CORS

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

CORS需要瀏覽器和服務(wù)器同時(shí)支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。整個(gè)CORS通信過程,都是瀏覽器自動(dòng)完成,不需要用戶參與。對(duì)于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請(qǐng)求跨源,就會(huì)自動(dòng)添加一些附加的頭信息,有時(shí)還會(huì)多出一次附加的請(qǐng)求,但用戶不會(huì)有感覺。

因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信。

CORS 分為簡單請(qǐng)求和非簡單請(qǐng)求,本文在這里實(shí)現(xiàn)簡單請(qǐng)求。(非簡單請(qǐng)求請(qǐng)參考
前端代碼:

<!DOCTYPE html>
<html>
<head>
    <title>news</title>
    <style type="text/css">
        .container{
            width: 900px;
            margin: 0 auto;
        }
    </style>
</head>
<body>
    <div class="container">
        <ul class="news">
        </ul>
        <button class="show">show news</button>
    </div>

    <script type="text/javascript">
        $('.show').addEventListener('click',function(){
            var xhr = new XMLHttpRequest();
            xhr.open('GET','http://a.ji.com:8080/getNews',true);
            xhr.send();
            xhr.onreadystatechange = function(){
                if(xhr.readyState === 4 && xhr.status === 200){
                    appendHtml(JSON.parse(xhr.responseText))
                }
            }
        })
        function appendHtml(news){
            var html = '';
            for(var i=0;i<news.length;i++){
                html+='<li>' + news[i] + '</li>';
            }
            console.log(html);
            $('.news').innerHTML = html;
        }
        function $(selector){
            return document.querySelector(selector)
        }
    </script>
</body>
</html>

后端代碼:

var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')


http.createServer(function(req,res){
    var pathObj = url.parse(req.url,true)

    switch(pathObj.pathname){
        case '/getNews':
            var news = [
            "第11日前瞻:中國沖擊4金 博爾特再戰(zhàn)200米羽球",
            "正直播柴飚/洪煒出戰(zhàn) 男雙力爭會(huì)師決賽",
            "女排將死磕巴西!郎平安排男陪練模仿對(duì)方核心"
            ]
            res.setHeader('Access-Control-Allow-Origin','http://b.ji.com:8080')
            res.end(JSON.stringify(news))

            break;

            default:
                fs.readFile(path.join(__dirname,pathObj.pathname),function(e,data){
                    if(e){
                        res.writeHead(404,'not found')
                        res.end('<h1>404 Not Found</h1>')
                    }else{
                        res.end(data)
                    }
                })
    }
}).listen(8080)

Access-Control-Allow-Origin字段是必須的,它的值要么是請(qǐng)求時(shí)Origin字段的值,要么是一個(gè)*,表示接受任意域名的請(qǐng)求。

降域

當(dāng)這兩個(gè)域名都屬于同一個(gè)基礎(chǔ)域名,而且所用的協(xié)議,端口都一致的時(shí)候,可以使用降域來實(shí)現(xiàn)跨域;當(dāng)以上所述成立的時(shí)候,可以通過將domain改成一樣的來實(shí)現(xiàn)。

a.html代碼:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>降域a</title>
    <style type="text/css">
        .ct{
            width: 910px;
            margin: auto;
        }
        .clearfix:after{
            content: "";
            display: block;
            clear: both;
        }
        .main{
            float: left;
            width: 450px;
            height: 300px;
            border: 1px solid #ccc;
        }
        .main input{
            margin: 20px;
            width: 200px;
        }
        .iframe{
            float: right;
        }
        iframe{
            width: 450px;
            height: 300px;
            border: 1px dashed #ccc;
        }
    </style>
</head>
<body>
    <div class="ct clearfix">
        <h1>使用降域?qū)崿F(xiàn)跨域</h1>
        <div class="main">
            <input type="text" placeholder="http://a.ji.com:8080/a.html">
        </div>

        <iframe src="http://b.ji.com:8080/b.html" frameborder="0"></iframe>
    </div>

    <script type="text/javascript">
        document.querySelector('.main input').addEventListener('input',function(){
            console.log(this.value);
            window.frame[0].document.querySelector('input').value = this.value;
        })
        document.domain ='ji.com'
    </script>
</body>
</html>

b.html代碼:

<!DOCTYPE html>
<html>
<head>
    <title>降域b</title>
    <style type="text/css">
        html,body{
            margin:0;
        }
        input{
            margin: 20px;
            width: 200px;
        }
    </style>
</head>
<body>
    <input type="text" id="input" placeholder="http://b.ji.com:8080/b.html">

    <script type="text/javascript">
        document.querySelector('#input').addEventListener('input',function(){
            window.parent.document.querySelector('input').value = this.value;
        })
        document.domain = 'ji.com'
    </script>
</body>
</html>

啟動(dòng)本地服務(wù)器,打開a.ji.com:8080/a.html ,window.frames[0].document.body可以取到body中的元素,
在非同源情況下,因?yàn)橥床呗缘南拗?,無法取到相應(yīng)元素,會(huì)報(bào)錯(cuò)。

postMessage

HTML5引入了一個(gè)全新的API:跨文檔通信 API(Cross-document messaging)。
這個(gè)API為window對(duì)象新增了一個(gè)window.postMessage方法,允許跨窗口通信,不論這兩個(gè)窗口是否同源。

a.html代碼:

<html>
<style>
    .ct{
        width: 910px;
        margin: auto;
    }
    .main{
        float: left;
        width: 450px;
        height: 300px;
        border: 1px solid #ccc;
    }
    .main input{
        margin: 20px;
        width: 200px;
    }
    .iframe{
        float: right;
    }
    iframe{
        width: 450px;
        height: 300px;
        border: 1px dashed #ccc;
    }
</style>

<div class="ct">
    <h1>使用postMessage實(shí)現(xiàn)跨域</h1>
    <div class="main">
        <input type="text" placeholder="http://a.ji.com:8080/a.html">
    </div>

    <iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>

</div>


<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
    console.log(this.value);
    window.frames[0].postMessage(this.value,'*');
})
window.addEventListener('message',function(e) {
        $('.main input').value = e.data
    console.log(e.data);
});
function $(id){
    return document.querySelector(id);
}
</script>
</html>

b.html代碼:

<html>
<style>
    html,body{
        margin: 0;
    }
    input{
        margin: 20px;
        width: 200px;
    }
</style>

    <input id="input" type="text"  placeholder="http://b.ji.com:8080/b.html">
<script>
// URL: http://b.jrg.com:8080/b.html
 
$('#input').addEventListener('input', function(){
    window.parent.postMessage(this.value, '*');
})
window.addEventListener('message',function(e) {
        $('#input').value = e.data
    console.log(e.data);
});
function $(id){
    return document.querySelector(id);
}   
</script>
</html>

postMessage方法的第一個(gè)參數(shù)是具體的信息內(nèi)容,第二個(gè)參數(shù)是接收消息的窗口的源(origin),即“協(xié)議 + 域名 + 端口”。也可以設(shè)為*,表示不限制域名,向所有窗口發(fā)送。
github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容