成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

關(guān)于跨域以及跨域的實(shí)現(xiàn)方式

崔曉明 / 2079人閱讀

摘要:關(guān)于跨域?yàn)槭裁磿?huì)有跨域我們得先了解下同源策略。簡(jiǎn)而言之,同協(xié)議同域名同端口號(hào)什么是跨域跨域就是采取技術(shù)方案突破同源策略的限制,實(shí)現(xiàn)不同域之間交互請(qǐng)求響應(yīng)。

關(guān)于跨域 why?

為什么會(huì)有跨域?

我們得先了解下 ==同源策略(SOP, Same Origin Policy)==。

瀏覽器出于安全方面的考慮,只能訪(fǎng)問(wèn)與包含它的頁(yè)面位于同一個(gè)域中的資源,該策略為通信設(shè)置了“相同的協(xié)議、相同的域、相同的端口”這一限制。試圖訪(fǎng)問(wèn)上述限制之外的資源,都會(huì)引發(fā)安全錯(cuò)誤。這種安全策略可以預(yù)防某些惡意行為。

簡(jiǎn)而言之,

同協(xié)議 Same Protocol

同域名 Same Hostname

同端口號(hào) Same Port

Same Protocol && Same Hostname && Same Port

What?

什么是跨域?

==跨域就是采取技術(shù)方案突破同源策略的限制,實(shí)現(xiàn)不同域之間交互(請(qǐng)求響應(yīng))。==

How?

那么如何實(shí)現(xiàn)跨域呢?
有以下幾種方法。

==方法一==

CORS (Cross-Origin Resource Sharing,跨域源資源共享),是一種ajax跨域請(qǐng)求資源的方式,支持現(xiàn)代瀏覽器,IE支持10以上,通過(guò)XMLHttpRequest實(shí)現(xiàn)Ajax通信的一個(gè)主要限制就是同源策略。
CORS是W3C的一個(gè)工作草案,定義了在必須訪(fǎng)問(wèn)跨境資源時(shí),瀏覽器和服務(wù)器該如何溝通。CORS的基本思想,就時(shí)使用自定義的HTTP頭部讓瀏覽器和服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或者響應(yīng)應(yīng)該成功還是失敗。
實(shí)現(xiàn)思路:使用XMLHttpRequest發(fā)送請(qǐng)求時(shí),瀏覽器會(huì)給該請(qǐng)求自動(dòng)添加一個(gè)請(qǐng)求頭:Origin。服務(wù)器經(jīng)過(guò)一系列處理,如果確定請(qǐng)求來(lái)源頁(yè)面屬于白名單,則在響應(yīng)頭部加入首部字段:Access-Control-Allow-Origin。瀏覽器比較請(qǐng)求頭部的Origin 和響應(yīng)頭部的 Access-Control-Allow-Origin是否一致,一致的話(huà),瀏覽器得到響應(yīng)數(shù)據(jù)。如果服務(wù)器沒(méi)有設(shè)置Access-Control-Allow-Origin 或者這個(gè)頭部源信息不匹配,瀏覽器就會(huì)駁回請(qǐng)求。

模擬CORS的實(shí)現(xiàn)

步驟1.

如何偽裝一個(gè)網(wǎng)站(在本地)?

1.編輯hosts文件

蘋(píng)果mac: 直接在git bash上輸入命令行操作即可 “sudo vi /etc.hosts” ,或者下載一些圖形界面應(yīng)用軟件直接修改。

Windows操作系統(tǒng):

win鍵(四個(gè)方塊的鍵)+ R = 彈開(kāi)運(yùn)行窗口

復(fù)制該文件路徑 c:windowssystem32driversetc

選中hosts文件,右鍵-屬性-安全-選擇組或用戶(hù)名(添加修改保存的權(quán)限的對(duì)象)- 編輯 - 再次選擇組或用戶(hù)名(添加修改保存的權(quán)限的對(duì)象 - 勾選權(quán)限(選項(xiàng)在此不表)

打開(kāi)hosts文件,寫(xiě)入 127.0.0.1 localhost;127.0.0.1 bai.com;127.0.0.1 google.com;可以寫(xiě)入你任何你想模擬的網(wǎng)站,按照這種對(duì)應(yīng)關(guān)系格式即可, ip地址+域名。

步驟2.
所需工具
node.js && git bash(模擬服務(wù)器),一個(gè)簡(jiǎn)單的html頁(yè)面里面有個(gè)跨域請(qǐng)求的Ajax通信。





    
    Google
    


    

hello world

//nodeJS模擬后端響應(yīng)CORS的實(shí)現(xiàn)
var http = require("http");
var fs = require("fs");
var url = require("url");
var path = require("path");

http.createServer(function(req, res){

     var urlObj = url.parse(req.url, true)

    switch (urlObj.pathname){

    case "/getNews":
    var news = ["NBA Break News","CBA Break News"]
    //CORS的實(shí)現(xiàn)
    res.setHeader("Access-Control-Allow-Origin","http://google.com:8080")
    /*res.setHeader("Access-Control-Allow-Origin","*")
    服務(wù)器設(shè)置公用接口
    */
    res.end(JSON.stringify(news));
    break;

    case "/" :
    if(urlObj.pathname == "/") {
        urlObj.pathname += "index.html"
    }

    default: 
    var filePath = path.join(__dirname, urlObj.pathname);
    fs.readFile(filePath,"binary", function(error, fileContent){
        if(error){
            console.log("404")
            res.writeHeader(404, "not found")
            res.end("

404,not found

") }else { res.write(fileContent, "binary") } }) } }).listen(8080);

上面代碼就是CORS實(shí)現(xiàn)的過(guò)程。

在本地修改hosts文件,127.0.0.1 google.com, 頁(yè)面的url為 http://google.com:8080。

在title為google的頁(yè)面上添加一個(gè)ajax請(qǐng)求,該請(qǐng)求以get方法會(huì)向baiduServer的端口("http://baidu.com:8080/getNews")發(fā)送一個(gè)請(qǐng)求。

瀏覽器會(huì)給請(qǐng)求頭加上Origin: http://google.com:8080, Request URL: http://baidu.com:8080/getNews。

baiduServer后端,響應(yīng)頭添加首部字段。Access-Control-Allow-Origin: http://google.com:8080。 表明該服務(wù)器(baiduServer)接受請(qǐng)求并給予響應(yīng)。

瀏覽器比較請(qǐng)求頭部的Origin 和響應(yīng)頭部的 Access-Control-Allow-Origin是否一致,一致的話(huà),瀏覽器得到響應(yīng)數(shù)據(jù)。如果服務(wù)器沒(méi)有設(shè)置Access-Control-Allow-Origin: http://google.com:8080 或者這個(gè)頭部源信息不匹配,瀏覽器就會(huì)駁回請(qǐng)求。

當(dāng)然服務(wù)器也可以設(shè)置公用接口, res.setHeader("Access-Control-Allow-Origin","*")

服務(wù)器設(shè)置公用接口, 任何人都可以使用該服務(wù)器這個(gè)端口的數(shù)據(jù)。


==方法二==

JSONP,是JSON with padding的簡(jiǎn)寫(xiě)(填充式JSON或參數(shù)式JSON)。

JSONP的原理,通過(guò)動(dòng)態(tài)

nodeJS

var http = require("http");
var fs = require("fs");
var path = require("path");
var url = require("url");

http.createServer(function(req,res){
    var urlObj = url.parse(req.url, true);
    switch(urlObj.pathname) {
        case "/getNews" :
        var news = ["NBA Break News","CBA Break News"];
        res.setHeader("Content-Type","text/javascript; charset=utf-8");
        if(urlObj.query.newsData){
            var data = urlObj.query.newsData + "(" + JSON.stringify(news) + ")" ;
            res.end(data);
        } else {
            res.end(JSON.stringify(news))
        }
        break;

        case "/" :
        if(urlObj.pathname == "/") {
            urlObj.pathname +=  "index.html"
        }

        default:
            fs.readFile(path.join(__dirname, urlObj.pathname), function(error, data) {
                if(error) {
                    res.writeHeader(404, "not found");
                    res.end("

404, Not Found

"); } else { res.end(data) } }); }; }).listen(8080);

==方法三==

降域,主要應(yīng)用場(chǎng)景是同一頁(yè)面下不同源的框架iframe請(qǐng)求

基于iframe實(shí)現(xiàn)的跨域,要求兩個(gè)域都必須屬于同一個(gè)基礎(chǔ)域, 比如 a.xx.com, b.xx.com,都有一個(gè)基礎(chǔ)域xx.com, 使用同一協(xié)議和端口,這樣在兩個(gè)頁(yè)面中同時(shí)添加documet.domain,就可以實(shí)現(xiàn)父頁(yè)面操控子頁(yè)面(框架)。

關(guān)于document.domain, 用來(lái)得到當(dāng)前網(wǎng)頁(yè)的域名。在瀏覽器輸入U(xiǎn)RL,wwww.baidu.com。 http://wwww.baidu.com, document.domain 為 "www.baidu.com"。 也可以為document.domain賦值, 不過(guò)有限制,就是前面提到的,只能賦值為當(dāng)前的域名或者基礎(chǔ)域名。
范例:

document.domain = "www.baidu.com" //successed 賦值成功, 當(dāng)前域名。

document.domain = "baidu.com" // successed 賦值成功, 基礎(chǔ)域名。

但是下面的賦值會(huì)報(bào)錯(cuò)(參數(shù)無(wú)效)。

"VM50:1 Uncaught DOMException: Failed to set the "domain" property on "Document": "a.baidu.com" is not a suffix of "www.baidu.com".

at :1:17"。

范例
document.domain = "google.com" // fail, 參數(shù)無(wú)效

document.domain = "a.baidu.com" // fail, 參數(shù)無(wú)效

因?yàn)間oogle.com 和 a.baidu.com不是當(dāng)前的域名,也不是當(dāng)前域名的基礎(chǔ)域名。
原因: 瀏覽器為了防止惡意修改document.domain來(lái)實(shí)現(xiàn)跨域偷取數(shù)據(jù)。

-- --
==模擬降域的實(shí)現(xiàn)==

錯(cuò)誤范例:

hosts 文件設(shè)置 win10系統(tǒng)路徑為 c:windowssystem32driversetchosts
127.0.0.1 a.com
127.0.0.1 b.com

a.com的一個(gè)網(wǎng)頁(yè)(a.html)里面 利用iframe引入一個(gè)b.com里的一個(gè)網(wǎng)頁(yè)(b.html )。在a.html里面可以看到b.html的內(nèi)容,但不能用Javascript來(lái)操作它。
原因: 這兩個(gè)頁(yè)面屬于不同的域,在操作之前,瀏覽器會(huì)檢測(cè)兩個(gè)頁(yè)面的域是否相等,相等則允許操作,不相等則報(bào)錯(cuò)。
這個(gè)例子里,不可能把a(bǔ).html與b.html,利用JS改成同一個(gè)域。原因:兩個(gè)域的基礎(chǔ)域名不相等。

在http://a.com:8080/a.html的控制臺(tái)(console), 輸入代碼window.frames[0].document.body //VM150:1 Uncaught DOMException: Blocked a frame with origin "http://a.com:8080" from accessing a cross-origin frame.

at :1:18




    
    a.com:8080/a.html


    





    
    b.com:8080/b.html


    

this is b.html

//nodeJS 
var http = require("http");
var fs = require("fs");
var path = require("path");
var url = require("url");

http.createServer(function(req,res){
    var urlObj = url.parse(req.url, true);
    switch(urlObj.pathname) {
        case "/getNews" :
        var news = ["NBA Break News","CBA Break News"];
        res.setHeader("Content-Type","text/javascript; charset=utf-8");
        if(urlObj.query.newsData){
            var data = urlObj.query.newsData + "(" + JSON.stringify(news) + ")" ;
            res.end(data);
        } else {
            res.end(JSON.stringify(news))
        }
        break;

        case "/" :
        if(urlObj.pathname == "/") {
            urlObj.pathname +=  "index.html"
        }

        default:
            var filePath = path.join(__dirname, "static" ,urlObj.pathname);
            console.log(filePath)
            fs.readFile(filePath, function(error, data) {
                if(error) {
                    res.writeHeader(404, "not found");
                    res.end("

404, Not Found

"); } else { res.end(data) } }); }; }).listen(8080);

可以把iframe的src改變?yōu)?http://a.com:8080/b.html",這樣就可以了,是不會(huì)有這個(gè)問(wèn)題的,因?yàn)橛蛳嗟取?br>控制臺(tái)不會(huì)報(bào)錯(cuò),但是這樣沒(méi)完成跨域??梢允褂胔tml5中的postMessage來(lái)實(shí)現(xiàn),針對(duì)基礎(chǔ)域不同的框架,這里暫且不表, 在方法四,會(huì)用到這種方法。

window.frames[0].document.body

?

?this is b.html ?

???




    
    a.com:8080/a.html


    
    

==正確范例:
降域的實(shí)現(xiàn)==

hosts文件設(shè)置

基礎(chǔ)域名相同

127.0.0.1 a.shawroc.com

127.0.0.1 b.shawroc.com

a.shawroc.com的里面一個(gè)網(wǎng)頁(yè)(a.html)引入b.shawroc.com里的一個(gè)網(wǎng)頁(yè)(b.html),a.shawroc.com還是不能操作b.shawroc.com里面的內(nèi)容。
原因:document.domain不一樣,a.shawroc.com vs b.shawroc.com。
但是兩個(gè)頁(yè)面的基礎(chǔ)域名是一樣的,通過(guò)JS,將兩個(gè)頁(yè)面的domain改成一樣。
在a.html 和 b.html 里都加入

這樣在兩個(gè)頁(yè)面中同時(shí)添加document.domain, 就可以實(shí)現(xiàn)父頁(yè)面操控子頁(yè)面(框架)。

控制臺(tái)
window.frames[0].document.body
//console輸出


    

this is http://b.shawroc.com:8080/b.html

代碼




    
    a.shawroc.com:8080


    
    
    




    
    b.shawroc.com:8080/b.html


    

this is http://b.shawroc.com:8080/b.html

==方法四==

html5的postMessage API

html5引入的postMessage()方法,允許來(lái)自不同源的腳本采用異步方式進(jìn)行有限的通信,可以實(shí)現(xiàn)跨文本檔、多窗口、跨域消息傳遞。

postMessage(data, origin) 方法,接受兩個(gè)參數(shù)。

1.data:要傳遞的數(shù)據(jù),html5規(guī)范中提到該參數(shù)可以是JavaScript的任意基本類(lèi)型或可復(fù)制的對(duì)象,然而并不是所有瀏覽器都做到了這點(diǎn)兒,部分瀏覽器只能處理字符串參數(shù),所以我們?cè)趥鬟f參數(shù)的時(shí)候需要使用JSON.stringify()方法對(duì)對(duì)象參數(shù)序列化,在低版本IE中引用json2.js可以實(shí)現(xiàn)類(lèi)似效果。

2.origin:字符串參數(shù),指明目標(biāo)窗口的源,協(xié)議+主機(jī)+端口號(hào)[+URL],URL會(huì)被忽略,所以可以不寫(xiě),這個(gè)參數(shù)是為了安全考慮,postMessage()方法只會(huì)將message傳遞給指定窗口,當(dāng)然如果愿意也可以建參數(shù)設(shè)置為"*",這樣可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話(huà)設(shè)置為"/"。

范例

模擬postMessage的工作機(jī)制

改寫(xiě)hosts文件

127.0.0.1 a.com

127.0.0.1 b.com






    
    a.com:8080


    




    
    b.shawroc.com:8080/b.html


    

this is http://b.com:8080/b.html

//nodeJS  模擬后端
var http = require("http");
var fs = require("fs");
var path = require("path");
var url = require("url");

http.createServer(function(req,res){
    var urlObj = url.parse(req.url, true);
    switch(urlObj.pathname) {
        case "/getNews" :
        var news = ["NBA Break News","CBA Break News"];
        res.setHeader("Content-Type","text/javascript; charset=utf-8");
        if(urlObj.query.newsData){
            var data = urlObj.query.newsData + "(" + JSON.stringify(news) + ")" ;
            res.end(data);
        } else {
            res.end(JSON.stringify(news))
        }
        break;

        case "/" :
        if(urlObj.pathname == "/") {
            urlObj.pathname +=  "index.html"
        }

        default:
            var filePath = path.join(__dirname, "postMessage" ,urlObj.pathname);
            fs.readFile(filePath, function(error, data) {
                if(error) {
                    res.writeHeader(404, "not found");
                    res.end("

404, Not Found

"); } else { res.end(data) } }); }; }).listen(8080);

解析代碼
步驟1, a.com:8080/a.html頁(yè)面下的input發(fā)生輸入事件時(shí), 向目標(biāo)窗口發(fā)一個(gè)MessageEvent事件