亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

js跨域的幾種解決方案

 更新時(shí)間:2024年05月06日 11:49:59   作者:muzidigbig  
本文主要介紹了js跨域的幾種解決方案,主要介紹了8種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、什么是跨域?

指的是瀏覽器不能執(zhí)行其他網(wǎng)站的腳本,簡(jiǎn)單來(lái)說(shuō)是瀏覽器同源政策的限制,瀏覽器針對(duì)于ajax的限制。

同源政策

兩個(gè)頁(yè)面擁有相同的  協(xié)議,域名,端口  就是同源,如果有一個(gè)不相同就是不同源。

同源政策產(chǎn)生的目的

保護(hù)用戶信息安全,防止一些網(wǎng)站盜取用戶信息。

常見(jiàn)跨域場(chǎng)景 

在這里插入圖片描述

二、跨域解決方案

1、通過(guò)jsonp跨域
2、跨域資源共享(CORS) 
3、document.domain + iframe跨域
4、location.hash + iframe
5、window.name + iframe跨域
6、postMessage跨域
7、nginx代理跨域
8、nodejs中間件代理跨域(非vue、vue(webpack--proxy代理))
9、WebSocket協(xié)議跨域

方案一:jsonp (json with padding)

原理:(回調(diào)函數(shù))

1.動(dòng)態(tài)創(chuàng)建一個(gè)script標(biāo)簽(無(wú)跨域限制) 

2.通過(guò)srcipt標(biāo)簽里的src屬性 進(jìn)行 跨域訪問(wèn)  屬性里面寫(xiě) 跨域的地址+ ?callback屬性作為函數(shù)傳遞給后端

3.服務(wù)端通過(guò)接收客戶端傳過(guò)來(lái)的callback屬性值對(duì)應(yīng)的函數(shù)  

4.要發(fā)生過(guò)去的數(shù)據(jù) 通過(guò)函數(shù)的參數(shù)傳遞過(guò)去 (JSON格式)

5.將函數(shù)名對(duì)應(yīng)調(diào)用的執(zhí)行代碼返回給客戶端(服務(wù)器端響應(yīng)數(shù)據(jù)一定是函數(shù)代碼的調(diào)用,當(dāng)瀏覽器對(duì)script將響應(yīng)內(nèi)容加載完成后,會(huì)自動(dòng)調(diào)用函數(shù))

6.并且在服務(wù)器端的執(zhí)行函數(shù)是字符串,字符串包裹著函數(shù)調(diào)用的代碼,如果沒(méi)有包裹,則就在服務(wù)端立即執(zhí)行。

缺點(diǎn):只能發(fā)送get請(qǐng)求,易受到 XSS攻擊

客戶端代碼

// 1.封裝一個(gè)jsonp函數(shù);
jsonp({
    // method: 'GET',// 所有的jsonp請(qǐng)求都是get請(qǐng)求,所以這個(gè)屬性可以不寫(xiě)了
    // data: , // 寫(xiě)了以后太繁瑣,取消
    url: 'http://www.localhost:3006/api/jsonp',
    success: function (res) {
        console.log(res)
    }
})
 
// 封裝
function jsonp(obj) {
    // 1.創(chuàng)建一個(gè)script標(biāo)簽;   2.改變src    3.給函數(shù)起名字,定義為全局函數(shù);
    var script = document.createElement("script"); // 不要用innerHTML, 他不會(huì)自動(dòng)發(fā)送請(qǐng)求
    // 3.給函數(shù)起名字,定義為全局函數(shù);
    var fnName = "haha_123123";
    // window.aaa就是把a(bǔ)aa設(shè)置為全局變量!
    window[fnName] = obj.success;
    // 2.改變src,添加到head中
    script.src = obj.url + "?callback=" + fnName;
    // 把script標(biāo)簽添加到head標(biāo)簽中,就會(huì)發(fā)送src的請(qǐng)求了
    document.head.appendChild(script);
    // 代碼執(zhí)行完畢,把script標(biāo)簽刪除
    script.onload = function () {
        document.head.removeChild(script);
        window[fnName] = undefined;
    }
}

服務(wù)端代碼

app.get("/api/jsonp", (req, res) => {
    // 獲取函數(shù)名,設(shè)置對(duì)象,發(fā)送給客戶端
    const fnName = req.query.callback;
    // 定義發(fā)送給客戶端的對(duì)象轉(zhuǎn)換為json字符串
    var objStr = JSON.stringify({
        name: '張三',
        age: 18
    });
    // 字符串類(lèi)型的執(zhí)行函數(shù)
    res.send(`${fnName}(${objStr})`);
});

 vue解決跨域問(wèn)題- vue-jsonp

// 第一步:在vue項(xiàng)目中安裝上vue-jsonp
npm i vue-jsonp --save

// 第二步:在vue項(xiàng)目中main.js導(dǎo)入
import { VueJsonp } from 'vue-jsonp'
Vue.use(VueJsonp)


// 使用示例:
this.$jsonp(url, {
    id: '1',
    output: "jsonp"
}).then(res => {
    console.log('res', res)
}).catch(err => {
    console.log(err)
})

方案二:CORS (CROSS-Origin Resource Sharing  跨域資源共享)

原理:服務(wù)器設(shè)置

同源政策默認(rèn)阻止跨域獲取資源,但是CORS給了web服務(wù)器權(quán)限,即服務(wù)器可以選擇,允許訪問(wèn)他們的資源 。

CORS需要瀏覽器和服務(wù)器同時(shí)支持,才可以實(shí)現(xiàn)跨域請(qǐng)求,目前幾乎所有瀏覽器都支持CORS,IE則不能低于IE10。CORS的整個(gè)過(guò)程都由瀏覽器自動(dòng)完成,前端無(wú)需做任何設(shè)置,跟平時(shí)發(fā)送ajax請(qǐng)求并無(wú)差異。so,實(shí)現(xiàn)CORS的關(guān)鍵在于服務(wù)器,只要服務(wù)器實(shí)現(xiàn)CORS接口,就可以實(shí)現(xiàn)跨域通信。

缺點(diǎn):忽略 cookie,瀏覽器版本有一定要求

屬于跨源 AJAX 請(qǐng)求的根本解決方法。相比 JSONP 只能發(fā)GET請(qǐng)求,CORS 允許任何類(lèi)型的請(qǐng)求。

分類(lèi):簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求 

條件:

1、請(qǐng)求方式:HEAD、GET、POST

2、請(qǐng)求頭信息:

        Accept
        Accept-Language
        Content-Language
        Last-Event-ID
        Content-Type 對(duì)應(yīng)的值是以下三個(gè)中的任意一個(gè)
                                application/x-www-form-urlencoded
                                multipart/form-data
                                text/plain

注意:同時(shí)滿足以上兩個(gè)條件時(shí),則是簡(jiǎn)單請(qǐng)求,否則為復(fù)雜請(qǐng)求

簡(jiǎn)單請(qǐng)求和復(fù)雜請(qǐng)求的區(qū)別:

簡(jiǎn)單請(qǐng)求:一次請(qǐng)求

復(fù)雜請(qǐng)求:兩次請(qǐng)求,在發(fā)送數(shù)據(jù)之前會(huì)先發(fā)第一次請(qǐng)求做‘預(yù)檢’,只有‘預(yù)檢’通過(guò)后才再發(fā)送一次請(qǐng)求用于數(shù)據(jù)傳輸。

關(guān)于預(yù)檢:

在發(fā)送真正的請(qǐng)求之前,會(huì)默認(rèn)發(fā)送一個(gè)options請(qǐng)求,做預(yù)檢,預(yù)檢成功后才發(fā)送真正的請(qǐng)求。

- 請(qǐng)求方式:OPTIONS
- “預(yù)檢”其實(shí)做檢查,檢查如果通過(guò)則允許傳輸數(shù)據(jù),檢查不通過(guò)則不再發(fā)送真正想要發(fā)送的消息
- 如何“預(yù)檢”
     => 如果復(fù)雜請(qǐng)求是PUT等請(qǐng)求,則服務(wù)端需要設(shè)置允許某請(qǐng)求,否則“預(yù)檢”不通過(guò)
        Access-Control-Request-Method
     => 如果復(fù)雜請(qǐng)求設(shè)置了請(qǐng)求頭,則服務(wù)端需要設(shè)置允許某請(qǐng)求頭,否則“預(yù)檢”不通過(guò)
        Access-Control-Request-Headers

實(shí)現(xiàn):設(shè)置三個(gè)響應(yīng)頭 

1.Access-Control-Allow-Origin 字段  (指定了哪些域名或ip地址可以跨域 也可以寫(xiě)* 代表允許所有地址;允許跨域訪問(wèn)的域名:若有端口需寫(xiě)全(協(xié)議+域名+端口),若沒(méi)有端口末尾不用加'/')

response.setHeater('Access-Control-Allow-Origin',' http://XXX.cn || * ||  ip地址  '); 

2.CORS默認(rèn)支持9個(gè)請(qǐng)求頭,如果在9個(gè)請(qǐng)求頭之外 要單獨(dú)設(shè)置,多個(gè)請(qǐng)求頭之間要用英文逗號(hào)隔開(kāi)。

// 提示OPTIONS預(yù)檢時(shí),后端需要設(shè)置的兩個(gè)常用自定義頭
response.setHeater('Access-Control-Allow-Headers','Content-Type,X-Requested-With'); 

3.默認(rèn)情況下CORS僅支持客戶端發(fā)起的GET、POST、HEAD請(qǐng)求,如果希望客戶端發(fā)送PUT、DELETE請(qǐng)求需要在服務(wù)器端設(shè)置

response.setHeater('Access-Control-Allow-Method','PUT'); 
// 允許前端帶認(rèn)證cookie:?jiǎn)⒂么隧?xiàng)后,上面的域名不能為'*',必須指定具體的域名,否則瀏覽器會(huì)提示
response.setHeader("Access-Control-Allow-Credentials", "true");

和跨域CORS有關(guān)的幾個(gè)請(qǐng)求頭和響應(yīng)頭請(qǐng)求頭 

請(qǐng)求頭響應(yīng)頭
OriginAccess-Control-Allow-Origin
Access-Control-Request-HeadersAccess-Control-Allow-Headers
Access-Control-Request-MethodAccess-Control-Allow-Methods
Access-Control-Allow-Credentials
Access-Control-Expose-Headers
Access-Control-Max-Age

請(qǐng)求頭:

  • Origin:當(dāng)前請(qǐng)求源,和響應(yīng)頭里的Access-Control-Allow-Origin 對(duì)標(biāo), 是否允許當(dāng)前源訪問(wèn),Origin是不可修改的
  • Access-Control-Request-Headers:本次真實(shí)請(qǐng)求的額外請(qǐng)求頭,和響應(yīng)頭里的Access-Control-Allow-Headers對(duì)標(biāo),是否允許真實(shí)請(qǐng)求的請(qǐng)求頭
  • Access-Control-Request-Method:本次真實(shí)請(qǐng)求的額外方法,和響應(yīng)頭里的Access-Control-Allow-Methods對(duì)標(biāo),是否允許真實(shí)請(qǐng)求使用的請(qǐng)求方法

響應(yīng)頭:

1.Access-Control-Allow-Credentials:

這里的Credentials(憑證)其意包括:Cookie ,授權(quán)標(biāo)頭或 TLS 客戶端證書(shū),默認(rèn)CORS請(qǐng)求是不帶Cookies的,這與JSONP不同,JSONP每次請(qǐng)求都攜帶Cookies的,當(dāng)然跨域允許帶Cookies會(huì)導(dǎo)致CSRF漏洞。如果非要跨域傳遞Cookies,web端需要給ajax設(shè)置withCredentials為true,同時(shí),服務(wù)器也必須使用Access-Control-Allow-Credentials頭響應(yīng)。此響應(yīng)頭true意味著服務(wù)器允許cookies(或其他用戶憑據(jù))包含在跨域請(qǐng)求中。另外,簡(jiǎn)單的GET請(qǐng)求是不預(yù)檢的,即使請(qǐng)求的時(shí)候設(shè)置widthCrenditials為true,如果響應(yīng)頭不帶Access-Control-Allow-Credentials,則會(huì)導(dǎo)致整個(gè)響應(yīng)資源被瀏覽器忽略。

2.Access-Control-Allow-Headers
3.Access-Control-Allow-Methods
4.Access-Control-Allow-Origin
5.Access-Control-Expose-Headers:

在CORS中,默認(rèn)的,只允許客戶端讀取下面六個(gè)響應(yīng)頭(在axios響應(yīng)對(duì)象的headers里能看到):

Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma

如果這六個(gè)以外的響應(yīng)頭要是想讓客戶端讀取到,就需要設(shè)置Access-Control-Expose-Headers這個(gè)為響應(yīng)頭名了,比如Access-Control-Expose-Headers: Token

6. Access-Control-Max-Age:設(shè)置預(yù)檢請(qǐng)求的有效時(shí)長(zhǎng),就是服務(wù)器允許的請(qǐng)求方法和請(qǐng)求頭做個(gè)緩存。

補(bǔ)充:

根據(jù)頭信息和請(qǐng)求方式,瀏覽器進(jìn)行了優(yōu)化,對(duì)請(qǐng)求進(jìn)行劃分,分為簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求 

簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求不做概述

簡(jiǎn)單請(qǐng)求 : 滿足瀏覽器請(qǐng)求會(huì)直接發(fā)送 并在請(qǐng)求頭中攜帶Origin 表示本次來(lái)自哪個(gè)源

如果不滿足簡(jiǎn)單請(qǐng)求 , 會(huì)在發(fā)送請(qǐng)求之前發(fā)送一個(gè)預(yù)檢請(qǐng)求 大小為0kb 請(qǐng)求方式為options  收到服務(wù)器響應(yīng)后,分析它是否支持跨域,如果支持跨域則直接發(fā)送,如果不支持 報(bào)錯(cuò)!

JSONP和CORS的區(qū)別:?JSONP:服務(wù)端不用修改,需要改前端。發(fā)jsonp請(qǐng)求?

?JSONP:只能發(fā)GET請(qǐng)求?

?CORS:前端的代碼不用修改,服務(wù)端的代碼需要修改。如果是簡(jiǎn)單請(qǐng)求的話在服務(wù)端加上一個(gè)響應(yīng)頭。?

?CORS:可以發(fā)任意請(qǐng)求

方案三:document.domain + iframe跨域 

此方案僅限主域相同,子域不同的跨域應(yīng)用場(chǎng)景。

通過(guò) iframe 是瀏覽器非同源標(biāo)簽,加載內(nèi)容中轉(zhuǎn),傳到當(dāng)前頁(yè)面的屬性中。

實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過(guò)js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域。

缺點(diǎn):同一一級(jí)域名;相同協(xié)議;相同端口

 1.)父窗口:(http://www.domain.com/a.html)

<iframe id="iframe" src="http://child.domain.com/b.html"></iframe>

<script> 
    document.domain = 'domain.com';
    var user = 'admin'; 
</script>

2.)子窗口:(http://child.domain.com/b.html)

<script> 
    document.domain = 'domain.com';
    // 獲取父窗口中變量
    alert('get js data from parent ---> ' + window.parent.user); 
</script>

方案四: location.hash + iframe跨域

實(shí)現(xiàn)原理: a欲與b跨域相互通信,通過(guò)中間頁(yè)c來(lái)實(shí)現(xiàn)。 三個(gè)頁(yè)面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問(wèn)來(lái)通信。

具體實(shí)現(xiàn):A域:a.html -> B域:b.html -> A域:c.html,a與b不同域只能通過(guò)hash值單向通信,b與c也不同域也只能單向通信,但c與a同域,所以c可通過(guò)parent.parent訪問(wèn)a頁(yè)面所有對(duì)象。

1.)a.html:(http://www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>

<script> 
    var iframe = document.getElementById('iframe');
    // 向b.html傳hash值
    setTimeout(function() {
        iframe.src = iframe.src + '#user=admin';
    }, 1000);

    // 開(kāi)放給同域c.html的回調(diào)方法
    function onCallback(res) {
        alert('data from c.html ---> ' + res);
    } 
</script>

2.)b.html:(http://www.domain2.com/b.html)

<iframe id="iframe" src="http://www.domain1.com/c.html" style="display:none;"></iframe>

<script> 
    var iframe = document.getElementById('iframe');

    // 監(jiān)聽(tīng)a.html傳來(lái)的hash值,再傳給c.html
    window.onhashchange = function () {
        iframe.src = iframe.src + location.hash;
    }; 
</script>

3.)c.html:(http://www.domain1.com/c.html)

<script> 
    // 監(jiān)聽(tīng)b.html傳來(lái)的hash值
    window.onhashchange = function () {
        // 再通過(guò)操作同域a.html的js回調(diào),將結(jié)果傳回
        window.parent.parent.onCallback('hello: ' + location.hash.replace('#user=', ''));
    }; 
</script>

方案五:window.name + iframe跨域

window.name屬性的獨(dú)特之處:name值在不同的頁(yè)面(甚至不同域名)加載后依舊存在,并且可以支持非常長(zhǎng)的 name 值(2MB)。

缺點(diǎn):頁(yè)面的屬性值有大小限制

1.)a.html:(http://www.domain1.com/a.html)

var proxy = function(url, callback) {
    var state = 0;
    var iframe = document.createElement('iframe');
    
    // 加載跨域頁(yè)面
    iframe.src = url;

    // onload事件會(huì)觸發(fā)2次,第1次加載跨域頁(yè),并留存數(shù)據(jù)于window.name
    iframe.onload = function() {
        if (state === 1) {
            // 第2次onload(同域proxy頁(yè))成功后,讀取同域window.name中數(shù)據(jù)
            callback(iframe.contentWindow.name);
            destoryFrame();

        } else if (state === 0) {
            // 第1次onload(跨域頁(yè))成功后,切換到同域代理頁(yè)面
            iframe.contentWindow.location = 'http://www.domain1.com/proxy.html';
            state = 1;
        }
    };

    document.body.appendChild(iframe);

    // 獲取數(shù)據(jù)以后銷(xiāo)毀這個(gè)iframe,釋放內(nèi)存;這也保證了安全(不被其他域frame js訪問(wèn))
    function destoryFrame() {
        iframe.contentWindow.document.write('');
        iframe.contentWindow.close();
        document.body.removeChild(iframe);
    }
}



// 請(qǐng)求跨域b頁(yè)面數(shù)據(jù)
proxy('http://www.domain2.com/b.html', function(data){
    alert(data);
});

2.)proxy.html:(http://www.domain1.com/proxy....中間代理頁(yè),與a.html同域,內(nèi)容為空即可。

3.)b.html:(http://www.domain2.com/b.html)

<script> window.name = 'This is domain2 data!'; </script>

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

方案六:postMessage跨域

postMessage是HTML5 XMLHttpRequest Level 2中的 API,且是為數(shù)不多可以跨域操作的window屬性之一,它可用于解決以下方面的問(wèn)題:
a.) 頁(yè)面和其打開(kāi)的新窗口的數(shù)據(jù)傳遞
b.) 多窗口之間消息傳遞
c.) 頁(yè)面與嵌套的iframe消息傳遞
d.) 上面三個(gè)場(chǎng)景的跨域數(shù)據(jù)傳遞

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

  • data: html5規(guī)范支持任意基本類(lèi)型或可復(fù)制的對(duì)象,但部分瀏覽器只支持字符串,所以傳參時(shí)最好用JSON.stringify()序列化。
  • origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*",表示可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話設(shè)置為"/"。

缺點(diǎn):瀏覽器版本要求,部分瀏覽器要配置放開(kāi)跨域限制

1.)a.html:(http://www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>

<script> 
    var iframe = document.getElementById('iframe');
    iframe.onload = function() {
        var data = {
        name: 'aym'
    };

    // 向domain2傳送跨域數(shù)據(jù)
    iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');


    // 接受domain2返回?cái)?shù)據(jù)
    window.addEventListener('message', function(e) {
        alert('data from domain2 ---> ' + e.data);
    }, false); 
</script>

2.)b.html:(http://www.domain2.com/b.html)

<script> 
    // 接收domain1的數(shù)據(jù)
    window.addEventListener('message', function(e) {
        alert('data from domain1 ---> ' + e.data);
        var data = JSON.parse(e.data);
        if (data) {
            data.number = 16;

            // 處理后再發(fā)回domain1
            window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
        }
    }, false); 
</script>

方案七:nginx 代理跨域(線上環(huán)境)

前端向發(fā)送請(qǐng)求,經(jīng)過(guò)代理,請(qǐng)求需要的服務(wù)器資源

缺點(diǎn):需要額外的代理服務(wù)器

1、nginx配置解決iconfont跨域

瀏覽器跨域訪問(wèn)js、css、img等常規(guī)靜態(tài)資源被同源策略許可,但iconfont字體文件(eot|otf|ttf|woff|svg)例外,此時(shí)可在nginx的靜態(tài)資源服務(wù)器中加入以下配置。

location / {
    add_header Access-Control-Allow-Origin *;
}

2、nginx反向代理接口跨域

跨域原理: 同源策略是瀏覽器的安全策略,不是HTTP協(xié)議的一部分。服務(wù)器端調(diào)用HTTP接口只是使用HTTP協(xié)議,不會(huì)執(zhí)行JS腳本,不需要同源策略,也就不存在跨越問(wèn)題。

實(shí)現(xiàn)思路:通過(guò)nginx配置一個(gè)代理服務(wù)器(域名與domain1相同,端口不同)做跳板機(jī),反向代理訪問(wèn)domain2接口,并且可以順便修改cookie中domain信息,方便當(dāng)前域cookie寫(xiě)入,實(shí)現(xiàn)跨域登錄。

nginx具體配置:

#proxy服務(wù)器
server {
    listen 81;
    server_name www.domain1.com;
    location / {
        proxy_pass   http://www.domain2.com:8080;  #反向代理
        proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
        index  index.html index.htm;

        # 當(dāng)用webpack-dev-server等中間件代理接口訪問(wèn)nignx時(shí),此時(shí)無(wú)瀏覽器參與,故沒(méi)有同源限制,下面的跨域配置可不啟用
        add_header Access-Control-Allow-Origin http://www.domain1.com;  #當(dāng)前端只跨域不帶cookie時(shí),可為*
        add_header Access-Control-Allow-Credentials true;
    }
}

1.) 前端代碼示例:

var xhr = new XMLHttpRequest();

// 前端開(kāi)關(guān):瀏覽器是否讀寫(xiě)cookie
xhr.withCredentials = true;

// 訪問(wèn)nginx中的代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();

2.) Nodejs后臺(tái)示例:

var http = require('http');
var server = http.createServer();
var qs = require('querystring');

server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));

// 向前臺(tái)寫(xiě)cookie
res.writeHead(200, {
    'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'   // HttpOnly:腳本無(wú)法讀取
});

res.write(JSON.stringify(params));
res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

方案八:Nodejs中間件代理跨域

node中間件實(shí)現(xiàn)跨域代理,原理大致與nginx相同,都是通過(guò)啟一個(gè)代理服務(wù)器,實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)發(fā),也可以通過(guò)設(shè)置cookieDomainRewrite參數(shù)修改響應(yīng)頭中cookie中域名,實(shí)現(xiàn)當(dāng)前域的cookie寫(xiě)入,方便接口登錄認(rèn)證。

1、 非vue框架的跨域(2次跨域)

利用 node + express + http-proxy-middleware 搭建一個(gè)proxy服務(wù)器。

1.)前端代碼示例:

var xhr = new XMLHttpRequest();

// 前端開(kāi)關(guān):瀏覽器是否讀寫(xiě)cookie
xhr.withCredentials = true;

// 訪問(wèn)http-proxy-middleware代理服務(wù)器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();

2.)中間件服務(wù)器:

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
// 代理跨域目標(biāo)接口
target: 'http://www.domain2.com:8080',
changeOrigin: true,

// 修改響應(yīng)頭信息,實(shí)現(xiàn)跨域并允許帶cookie
onProxyRes: function(proxyRes, req, res) {
    res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
    res.header('Access-Control-Allow-Credentials', 'true');
},

// 修改響應(yīng)信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com'  // 可以為false,表示不修改
}));

app.listen(3000);
console.log('Proxy server is listen at port 3000...');

3.)Nodejs后臺(tái)同(七:nginx)

2、 vue框架的跨域 -- Proxy代理(1次跨域,開(kāi)發(fā)環(huán)境)

通過(guò)服務(wù)器代理去發(fā)ajax 這個(gè)代理是webpack提供給我們的  因?yàn)榉?wù)器只是對(duì)ajax的同源限制,并不會(huì)限制服務(wù)器之間的通信。

利用 node + webpack + webpack-dev-server 代理接口跨域。在開(kāi)發(fā)環(huán)境下,由于vue渲染服務(wù)和接口代理服務(wù)都是webpack-dev-server同一個(gè),所以頁(yè)面與代理接口之間不再跨域,無(wú)須設(shè)置headers 跨域信息了。

使用環(huán)境:開(kāi)發(fā)環(huán)境

生產(chǎn)環(huán)境:nginx

webpack.config.js / vue.config.js 部分配置:

//vue.config.js

module.exports = {
entry: {},
module: {},
...

devServer: {
    proxy: {
      // 如果請(qǐng)求地址以/api打頭,就出觸發(fā)代理機(jī)制
      // http://localhost:8080/api/login -> http://localhost:3000/api/login
      '/api': { // 攔截以 /api 開(kāi)頭的url接口
        target: 'http://localhost:3000', //目標(biāo)接口域名  路徑:http://localhost:3000/api
        changeOrigin: true, //是否跨域
        ws: true, //如果要代理 websockets,配置這個(gè)參數(shù)
        secure: false, // 如果是https接口,需要配置這個(gè)參數(shù)
        //     // 標(biāo)識(shí)替換
        //     // 原請(qǐng)求地址為 /api/getData 將'/api'替換''時(shí),
        //     // 代理后的請(qǐng)求地址為: http://xxx.xxx.xxx/getData
        //     // 若替換為'/other',則代理后的請(qǐng)求地址為 http://xxx.xxx.xxx/other/getData 
        pathRewrite: { // 標(biāo)識(shí)替換
            '^/api': '/'   //重寫(xiě)接口 后臺(tái)接口指向不統(tǒng)一  所以指向所有/   路徑:http://localhost:3000/
        //       '^/api': '/api/mock'
        }
    }
  },

}

方案九:WebSocket 協(xié)議跨域

  • WebSocket protocol是HTML5一種新的協(xié)議(WebSocket協(xié)議本質(zhì)上是一個(gè)基于tcp的協(xié)議)。
  • 它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信,能更好的節(jié)省服務(wù)器資源和帶寬并達(dá)到實(shí)時(shí)通訊的目的,同時(shí)允許跨域通訊,是server push技術(shù)的一種很好的實(shí)現(xiàn)。
  • Websocket是一個(gè)持久化的協(xié)議

WebSocket 的原理

  • WebSocket約定了一個(gè)通信的規(guī)范,通過(guò)單個(gè)TCP一個(gè)握手的機(jī)制,客戶端和服務(wù)器之間能建立一個(gè)類(lèi)似tcp的連接,從而方便它們之間的通信
  • 在WebSocket出現(xiàn)之前,web交互一般是基于http協(xié)議的短連接或者長(zhǎng)連接
  • WebSocket是一種全新的協(xié)議,不屬于http無(wú)狀態(tài)協(xié)議,協(xié)議名為"ws"

有什么用: WebSocket用來(lái)解決http不能持久連接的問(wèn)題,因?yàn)榭梢噪p向通信所以可以用來(lái)實(shí)現(xiàn)聊天室,以及其他由服務(wù)端主動(dòng)推送的功能例如 實(shí)時(shí)天氣、股票報(bào)價(jià)、余票顯示、消息通知等。 

WebSocket與http的關(guān)系 

相同點(diǎn):

  • 都是基于tcp的,都是可靠性傳輸協(xié)議
  • 都是應(yīng)用層協(xié)議

不同點(diǎn):

  • WebSocket是單個(gè) TCP 連接上進(jìn)行雙向通信協(xié)議,模擬Socket協(xié)議,可以雙向發(fā)送或接受信息
  • HTTP是單向的
  • WebSocket是需要瀏覽器和服務(wù)器一次握手進(jìn)行建立連接的
  • 而http是瀏覽器發(fā)起向服務(wù)器的連接,服務(wù)器預(yù)先并不知道這個(gè)連接

 聯(lián)系:

WebSocket在建立握手時(shí),數(shù)據(jù)是通過(guò)HTTP傳輸?shù)?。但是建立之后,在真正傳輸時(shí)候是不需要HTTP協(xié)議的

總結(jié)(總體過(guò)程):

首先,客戶端發(fā)起http請(qǐng)求,經(jīng)過(guò)3次握手后,建立起TCP連接;http請(qǐng)求里存放WebSocket支持的版本號(hào)等信息,如:Upgrade、Connection、WebSocket-Version等;

然后,服務(wù)器收到客戶端的握手請(qǐng)求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù);

最后,客戶端收到連接成功的消息后,開(kāi)始借助于TCP傳輸信道進(jìn)行全雙工通信。

WebSocket有以下特點(diǎn):

  • 是真正的全雙工方式,建立連接后客戶端與服務(wù)器端是完全平等的,可以互相主動(dòng)請(qǐng)求。而HTTP長(zhǎng)連接基于HTTP,是傳統(tǒng)的客戶端對(duì)服務(wù)器發(fā)起請(qǐng)求的模式。
  • HTTP長(zhǎng)連接中,每次數(shù)據(jù)交換除了真正的數(shù)據(jù)部分外,服務(wù)器和客戶端還要大量交換HTTP header,信息交換效率很低。
    Websocket協(xié)議通過(guò)第一個(gè)request建立了TCP連接之后,之后交換的數(shù)據(jù)都不需要發(fā)送 HTTP header就能交換數(shù)據(jù),這顯然和原有的HTTP協(xié)議有區(qū)別所以它需要對(duì)服務(wù)器和客戶端都進(jìn)行升級(jí)才能實(shí)現(xiàn)(主流瀏覽器都已支持HTML5)
var socket = new WebSocket("ws://example.com/socketserver");
 
socket.onopen = function(event) {
  socket.send("Hello server!");
};
 
socket.onmessage = function(event) {
  console.log("Received message from server: " + event.data);
};
 
socket.onerror = function(event) {
  console.log("WebSocket error: " + event.error);
};
 
socket.onclose = function(event) {
  console.log("WebSocket connection closed with code " + event.code);
};

 在上面的代碼中,首先創(chuàng)建了一個(gè) WebSocket 對(duì)象,指定了服務(wù)器的地址。然后在 onopen 回調(diào)函數(shù)中,發(fā)送了一個(gè)消息到服務(wù)器。當(dāng)服務(wù)器發(fā)送消息到客戶端時(shí),onmessage 回調(diào)函數(shù)會(huì)被觸發(fā),從而可以處理服務(wù)器發(fā)送的消息。如果出現(xiàn)錯(cuò)誤或者連接被關(guān)閉,onerror 和 onclose 回調(diào)函數(shù)會(huì)被觸發(fā),從而可以處理這些事件。

需要注意的是,在使用 WebSocket 發(fā)送消息之前,必須先建立 WebSocket 連接。在上面的代碼中,通過(guò)創(chuàng)建一個(gè) WebSocket 對(duì)象來(lái)建立連接,然后在 onopen 回調(diào)函數(shù)中發(fā)送消息到服務(wù)器。如果在連接建立之前就嘗試發(fā)送消息,那么這些消息將無(wú)法發(fā)送成功。

vue怎么用websocket發(fā)送請(qǐng)求 

<template>
  <div>
    <input v-model="message" @keyup.enter="sendMessage">
    <button @click="sendMessage">Send</button>
    <ul>
      <li v-for="msg in messages">{{ msg }}</li>
    </ul>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      message: '',
      messages: [],
      socket: null
    }
  },
  created() {
    this.socket = new WebSocket('ws://localhost:3000')
    // 監(jiān)聽(tīng)服務(wù)器端響應(yīng)處理函數(shù)
    this.socket.addEventListener('message', this.handleMessage)
    /*
    this.$once('hook:beforeDestroy',() => {
        // 關(guān)閉連接
        this.socket.close()
    })
    */
  },
  beforeDestroy() {
    // 關(guān)閉連接
    this.socket.close()
  },
  methods: {
    sendMessage() {
      // 向服務(wù)器端發(fā)送消息
      this.socket.send(this.message)
      this.message = ''
    },
    handleMessage(event) {
      this.messages.push(event.data)
    }
  }
}
</script>

在上面的代碼中,

  • 首先在 created 鉤子函數(shù)中創(chuàng)建了一個(gè) WebSocket 對(duì)象,并添加了一個(gè)消息處理函數(shù) handleMessage。
  • 在 sendMessage 方法中,將用戶輸入的消息發(fā)送到服務(wù)器。
  • 在 handleMessage 方法中,將從服務(wù)器接收到的消息添加到 messages 數(shù)組中,從而可以在界面上顯示出來(lái)。
  • 在組件銷(xiāo)毀之前,需要調(diào)用 close() 方法關(guān)閉 WebSocket 連接。

需要注意的是,在使用 WebSocket 時(shí),需要考慮跨域問(wèn)題。如果 WebSocket 連接的 URL 和當(dāng)前頁(yè)面的 URL 不同,那么需要在服務(wù)器端進(jìn)行相應(yīng)的跨域設(shè)置。同時(shí),需要注意在發(fā)送和接收消息時(shí)的數(shù)據(jù)格式,通常需要將數(shù)據(jù)轉(zhuǎn)換為 JSON 字符串進(jìn)行傳輸也支持二進(jìn)制。

原生WebSocket API 使用起來(lái)不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡(jiǎn)單、靈活的接口,也對(duì)不支持webSocket的瀏覽器提供了向下兼容。

缺點(diǎn):瀏覽器一定版本要求,服務(wù)器需要支持 websocket 協(xié)議 

1.)前端代碼:

<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>

<script> 

// var socket = io('http://www.domain2.com:8080');
// 創(chuàng)建Socket.IO實(shí)例,建立連接
var socket= new io.Socket('localhost',{
    port: 8080
});

socket.connect();

// 連接成功處理
socket.on('connect', function() {
    console.log('Client has connected to the server!');
});
// 監(jiān)聽(tīng)服務(wù)端消息
socket.on('message', function(msg) {
    console.log('data from server: ---> ' + msg);
});

// 監(jiān)聽(tīng)服務(wù)端關(guān)閉
socket.on('disconnect', function() { 
    console.log('Server socket has closed.'); 
});


document.getElementsByTagName('input')[0].onblur = function() {
    // 通過(guò)Socket發(fā)送一條消息到服務(wù)器
    socket.send(this.value);
}; 
</script>

2.)Nodejs socket后臺(tái):

var http = require('http');
var socket = require('socket.io');

// 啟http服務(wù)
var server = http.createServer(function(req, res) {
res.writeHead(200, {
'Content-type': 'text/html'
});
res.end();
});

server.listen('8080');
console.log('Server is running at port 8080...');

// 監(jiān)聽(tīng)socket連接
socket.listen(server).on('connection', function(client) {
// 接收信息
client.on('message', function(msg) {
client.send('hello:' + msg);
console.log('data from client: ---> ' + msg);
});

// 斷開(kāi)處理
client.on('disconnect', function() {
    console.log('Client socket has closed.'); 
});

到此這篇關(guān)于js跨域的幾種解決方案的文章就介紹到這了,更多相關(guān)js跨域內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 如何檢查一個(gè)對(duì)象是否為空

    如何檢查一個(gè)對(duì)象是否為空

    這篇文章主要介紹了js如何檢查一個(gè)對(duì)象是否為空,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 解決BootStrap Fileinput手機(jī)圖片上傳顯示旋轉(zhuǎn)問(wèn)題

    解決BootStrap Fileinput手機(jī)圖片上傳顯示旋轉(zhuǎn)問(wèn)題

    這篇文章主要介紹了 BootStrap Fileinput手機(jī)圖片上傳顯示旋轉(zhuǎn)問(wèn)題,需要的朋友可以參考下
    2017-06-06
  • 微信小程序國(guó)際化探索實(shí)現(xiàn)(附源碼地址)

    微信小程序國(guó)際化探索實(shí)現(xiàn)(附源碼地址)

    這篇文章主要介紹了微信小程序國(guó)際化探索實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • js時(shí)鐘翻牌效果實(shí)現(xiàn)代碼分享

    js時(shí)鐘翻牌效果實(shí)現(xiàn)代碼分享

    這篇文章主要介紹了javascript時(shí)鐘翻牌效果的實(shí)現(xiàn),效果非常酷炫,實(shí)現(xiàn)步驟也很簡(jiǎn)單,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2015-08-08
  • 11個(gè)Javascript小技巧幫你提升代碼質(zhì)量(小結(jié))

    11個(gè)Javascript小技巧幫你提升代碼質(zhì)量(小結(jié))

    這篇文章主要介紹了11個(gè)Javascript小技巧幫你提升代碼質(zhì)量(小結(jié)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • JS實(shí)現(xiàn)微信搖一搖原理解析

    JS實(shí)現(xiàn)微信搖一搖原理解析

    這篇文章主要為大家詳細(xì)介紹了JS微信搖一搖的實(shí)現(xiàn)原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-07-07
  • 從延遲處理解析JavaScript惰性編程

    從延遲處理解析JavaScript惰性編程

    這篇文章主要為大家介紹了從延遲處理解析JavaScript惰性編程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • setInterval和setTimeout停止的方法

    setInterval和setTimeout停止的方法

    要想知道它們是怎么停止的,首先我們要了解它們的運(yùn)行機(jī)制和原理,下面是具體的介紹。
    2011-01-01
  • 新發(fā)現(xiàn)一個(gè)騙鏈接的方法(js讀取cookies)

    新發(fā)現(xiàn)一個(gè)騙鏈接的方法(js讀取cookies)

    新發(fā)現(xiàn)一個(gè)騙鏈接的方法(js讀取cookies),需要的朋友可以參考下。
    2012-01-01
  • JS動(dòng)態(tài)修改表格cellPadding和cellSpacing的方法

    JS動(dòng)態(tài)修改表格cellPadding和cellSpacing的方法

    這篇文章主要介紹了JS動(dòng)態(tài)修改表格cellPadding和cellSpacing的方法,涉及javascript操作cellPadding和cellSpacing屬性的技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-03-03

最新評(píng)論