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

資訊專欄INFORMATION COLUMN

九種 “姿勢(shì)” 讓你徹底解決跨域問題

charles_paul / 2083人閱讀

摘要:什么是跨域當(dāng)協(xié)議域名端口號(hào),有一個(gè)或多個(gè)不同時(shí),有希望可以訪問并獲取數(shù)據(jù)的現(xiàn)象稱為跨域訪問,同源策略限制下都是不支持跨域的。命名是隨意的,只要是符合一級(jí)域名與二級(jí)域名的關(guān)系即可,然后訪問。


閱讀原文


同源策略

同源策略/SOP(Same origin policy)是一種約定,由 Netscape 公司 1995 年引入瀏覽器,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,瀏覽器很容易受到 XSS、CSRF 等攻擊。所謂同源是指 "協(xié)議 + 域名 + 端口" 三者相同,即便兩個(gè)不同的域名指向同一個(gè) ip 地址,也非同源。


什么是跨域?

當(dāng)協(xié)議、域名、端口號(hào),有一個(gè)或多個(gè)不同時(shí),有希望可以訪問并獲取數(shù)據(jù)的現(xiàn)象稱為跨域訪問,同源策略限制下 cookie、localStoragedom、ajaxIndexDB 都是不支持跨域的。

假設(shè) cookie 支持了跨域,http 協(xié)議無狀態(tài),當(dāng)用戶訪問了一個(gè)銀行網(wǎng)站登錄后,銀行網(wǎng)站的服務(wù)器給返回了一個(gè) sessionId,當(dāng)通過當(dāng)前瀏覽器再訪問一個(gè)惡意網(wǎng)站,如果 cookie 支持跨域,惡意網(wǎng)站將獲取 sessionId 并訪問銀行網(wǎng)站,出現(xiàn)安全性問題;IndexDB、localStorage 等數(shù)據(jù)存儲(chǔ)在不同域的頁(yè)面切換時(shí)是獲取不到的;假設(shè) dom 元素可以跨域,在自己的頁(yè)面寫入一個(gè) iframe 內(nèi)部嵌入的地址是 www.baidu.com,當(dāng)在百度頁(yè)面登錄賬號(hào)密碼時(shí)就可以在自己的頁(yè)面獲取百度的數(shù)據(jù)信息,這顯然是不合理的。

這就是為什么 cookie、localStoragedom、ajaxIndexDB 會(huì)受到同源策略會(huì)限制,下面還有一點(diǎn)對(duì)跨域理解的誤區(qū):

誤區(qū):同源策略限制下,訪問不到后臺(tái)服務(wù)器的數(shù)據(jù),或訪問到后臺(tái)服務(wù)器的數(shù)據(jù)后沒有返回;
正確:同源策略限制下,可以訪問到后臺(tái)服務(wù)器的數(shù)據(jù),后臺(tái)服務(wù)器會(huì)正常返回?cái)?shù)據(jù),而被瀏覽器給攔截了。


實(shí)現(xiàn)跨域的方式 一、使用 jsonp 跨域

使用場(chǎng)景:當(dāng)自己的項(xiàng)目前端資源和后端部署在不同的服務(wù)器地址上,或者其他的公司需要訪問自己對(duì)外公開的接口,需要實(shí)現(xiàn)跨域獲取數(shù)據(jù),如百度搜索。

// 封裝 jsonp 跨域請(qǐng)求的方法
function jsonp({ url, params, cb }) {
    return new Promise((resolve, reject) => {
        // 創(chuàng)建一個(gè) script 標(biāo)簽幫助我們發(fā)送請(qǐng)求
        let script = document.createElement("script");
        let arr = [];
        params = { ...params, cb };

        // 循環(huán)構(gòu)建鍵值對(duì)形式的參數(shù)
        for (let key in params) {
            arr.push(`${key}=${params[key]}`);
        }

        // 創(chuàng)建全局函數(shù)
        window[cb] = function(data) {
            resolve(data);
            // 在跨域拿到數(shù)據(jù)以后將 script 標(biāo)簽銷毀
            document.body.removeChild(script);
        };

        // 拼接發(fā)送請(qǐng)求的參數(shù)并賦值到 src 屬性
        script.src = `${url}?${arr.join("&")}`;
        document.body.appendChild(script);
    });
}

// 調(diào)用方法跨域請(qǐng)求百度搜索的接口
json({
    url: "https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su",
    params: {
        wd: "jsonp"
    },
    cb: "show"
}).then(data => {
    // 打印請(qǐng)求回的數(shù)據(jù)
    console.log(data);
});

缺點(diǎn):

只能發(fā)送 get 請(qǐng)求 不支持 post、put、delete;

不安全,容易引發(fā) xss 攻擊,別人在返回的結(jié)果中返回了下面代碼。

`let script = document.createElement("script");
script.src = "http://192.168.0.57:8080/xss.js";
document.body.appendChild(script);`;

會(huì)把別人的腳本引入到自己的頁(yè)面中執(zhí)行,如:彈窗、廣告等,甚至更危險(xiǎn)的腳本程序。


二、使用 CORS 跨域

跨源資源共享/CORS(Cross-Origin Resource Sharing)是 W3C 的一個(gè)工作草案,定義了在必須訪問跨源資源時(shí),瀏覽器與服務(wù)器應(yīng)該如何溝通。CORS 背后的基本思想,就是使用自定義的 HTTP 頭部讓瀏覽器與服務(wù)器進(jìn)行溝通,從而決定請(qǐng)求或響應(yīng)是應(yīng)該成功,還是應(yīng)該失敗。

使用場(chǎng)景:多用于開發(fā)時(shí),前端與后臺(tái)在不同的 ip 地址下進(jìn)行數(shù)據(jù)訪問。

現(xiàn)在啟動(dòng)兩個(gè)端口號(hào)不同的服務(wù)器,創(chuàng)建跨域條件,服務(wù)器(NodeJS)代碼如下:

// 服務(wù)器1
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);

// 服務(wù)器2
const express = require("express");
let app = express();
app.get("/getDate", function(req, res) {
    res.end("I love you");
});
app.use(express.static(__dirname));
app.listen(4000);

由于我們的 NodeJS 服務(wù)器使用 express 框架,在我們的項(xiàng)目根目錄下的命令行中輸入下面代碼進(jìn)行安裝:

npm install express --save

通過訪問 http://localhost:3000/index.html 獲取 index.html 文件并執(zhí)行其中的 Ajax 請(qǐng)求 http://localhost:4000/getDate 接口去獲取數(shù)據(jù),index.html 文件內(nèi)容如下:





    
    CORS 跨域


    

上面 index.html 代碼中發(fā)送請(qǐng)求訪問不在同源的服務(wù)器 2,此時(shí)會(huì)在控制臺(tái)給出錯(cuò)誤信息,告訴我們?nèi)鄙倭四男╉憫?yīng)頭,我們對(duì)應(yīng)報(bào)錯(cuò)信息去修改訪問的服務(wù)器 2 的代碼,添加對(duì)應(yīng)的響應(yīng)頭,實(shí)現(xiàn) CORS 跨域。

// 服務(wù)器2
const express = require("express");
let app = express();

// 允許訪問域的白名單
let whiteList = ["http://localhost:3000"];

app.use(function(req, res, next) {
    let origin = req.header.origin;
    if (whiteList.includes(origin)) {
        // 設(shè)置那個(gè)源可以訪問我,參數(shù)為 * 時(shí),允許任何人訪問,但是不可以和 cookie 憑證的響應(yīng)頭共同使用
        res.setHeader("Access-Control-Allow-Origin", origin);
        // 想要獲取 ajax 的頭信息,需設(shè)置響應(yīng)頭
        res.setHeader("Access-Control-Allow-Headers", "name");
        // 處理復(fù)雜請(qǐng)求的頭
        res.setHeader("Access-Control-Allow-Methods", "PUT");
        // 允許發(fā)送 cookie 憑證的響應(yīng)頭
        res.setHeader("Access-Control-Allow-Credentials", true);
        // 允許前端獲取哪個(gè)頭信息
        res.setHeader("Access-Control-Expose-Headers", "name");
        // 處理 OPTIONS 預(yù)檢的存活時(shí)間,單位 s
        res.setHeader("Access-Control-Max-Age", 5);
        // 發(fā)送 PUT 請(qǐng)求會(huì)做一個(gè)試探性的請(qǐng)求 OPTIONS,其實(shí)是請(qǐng)求了兩次,當(dāng)接收的請(qǐng)求為 OPTIONS 時(shí)不做任何處理
        if (req.method === "OPTIONS") {
            res.end();
        }
    }
    next();
});

app.put("/getDate", function(req, res) {
    // res.setHeader("name", "nihao"); // 設(shè)置自定義響應(yīng)頭信息
    res.end("I love you");
});

app.get("/getDate", function(req, res) {
    res.end("I love you");
});

app.use(express.static(__dirname));
app.listen(4000);


三、使用 postMessage 實(shí)現(xiàn)跨域

postMessage 是 H5 的新 API,跨文檔消息傳送(cross-document messaging),有時(shí)候簡(jiǎn)稱為 XMD,指的是在來自不同域的頁(yè)面間傳遞消息。

調(diào)用方式:window.postMessage(message, targetOrigin)

message:發(fā)送的數(shù)據(jù)

targetOrigin:發(fā)送的窗口的域

在對(duì)應(yīng)的頁(yè)面中用 message 事件接收,事件對(duì)象中有 data、originsource 三個(gè)重要信息

data:接收到的數(shù)據(jù)

origin:接收到數(shù)據(jù)源的域(數(shù)據(jù)來自哪個(gè)域)

source:接收到數(shù)據(jù)源的窗口對(duì)象(數(shù)據(jù)來自哪個(gè)窗口對(duì)象)

使用場(chǎng)景:不是使用 Ajax 的數(shù)據(jù)通信,更多是在兩個(gè)頁(yè)面之間的通信,在 A 頁(yè)面中引入 B 頁(yè)面,在 AB 兩個(gè)頁(yè)面之間通信。

與上面 CORS 類似,我們要?jiǎng)?chuàng)建跨域場(chǎng)景,搭建兩個(gè)端口號(hào)不同的 Nodejs 服務(wù)器,后面相同方式就不多贅述了。

// 服務(wù)器1
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);

// 服務(wù)器2
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(4000);

通過訪問 http://localhost:3000/a.html,在 a.html 中使用 iframe 標(biāo)簽引入 http://localhost:4000/b.html,在兩個(gè)窗口間傳遞數(shù)據(jù)。





    
    頁(yè)面 A


    
    





    
    頁(yè)面 B


    


四、使用 window.name 實(shí)現(xiàn)跨域

同樣是頁(yè)面之間的通信,需要借助 iframe 標(biāo)簽,A 頁(yè)面和 B 頁(yè)面是同域的 http://localhost:3000,C 頁(yè)面在獨(dú)立的域 http://localhost:4000。

// 服務(wù)器1
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);

// 服務(wù)器2
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(4000);

實(shí)現(xiàn)思路:在 A 頁(yè)面中將 iframesrc 指向 C 頁(yè)面,在 C 頁(yè)面中將屬性值存入 window.name 中,再把 iframesrc 換成同域的 B 頁(yè)面,在當(dāng)前的 iframewindow 對(duì)象中取出 name 的值,訪問 http://localhost:3000/a.html。





    
    頁(yè)面 A


    
    





    
    頁(yè)面 C


    


五、使用 location.hash 實(shí)現(xiàn)跨域

window.name 跨域的情況相同,是不同域的頁(yè)面間的參數(shù)傳遞,需要借助 iframe 標(biāo)簽,A 頁(yè)面和 B 頁(yè)面是同域的 http://localhost:3000,C 頁(yè)面是獨(dú)立的域 http://localhost:4000。

// 服務(wù)器1
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(3000);

// 服務(wù)器2
const express = require(express);
let app = express();
app.use(express.static(__dirname));
app.listen(4000);

實(shí)現(xiàn)思路:A 頁(yè)面通過 iframe 引入 C 頁(yè)面,并給 C 頁(yè)面?zhèn)饕粋€(gè) hash 值,C 頁(yè)面收到 hash 值后創(chuàng)建 iframe 引入 B 頁(yè)面,把 hash 值傳給 B 頁(yè)面,B 頁(yè)面將自己的 hash 值放在 A 頁(yè)面的 hash 值中,訪問 http://localhost:3000/a.html。





    
    頁(yè)面 A


    
    





    
    頁(yè)面 C


    





    
    頁(yè)面 B


    


六、使用 document.domain 實(shí)現(xiàn)跨域

使用場(chǎng)景:不是萬能的跨域方式,大多使用于同一公司不同產(chǎn)品間獲取數(shù)據(jù),必須是一級(jí)域名和二級(jí)域名的關(guān)系,如 www.baidu.com 與 video.baidu.com 之間。

const express = require("express");
let app = express();

app.use(express.static(__dirname));
app.listen(3000);

想要模擬使用 document.domain 跨域的場(chǎng)景需要做些小小的準(zhǔn)備,到 C:WindowsSystem32driversetc 該路徑下找到 hosts 文件,在最下面創(chuàng)建一個(gè)一級(jí)域名和一個(gè)二級(jí)域名。

127.0.0.1 ???????? www.domainacross.com
127.0.0.1 ???????? sub.domainacross.com

命名是隨意的,只要是符合一級(jí)域名與 二級(jí)域名的關(guān)系即可,然后訪問 http://www.domainacross.com:3000/a.html。





    
    頁(yè)面 A


    

我是頁(yè)面 A 的內(nèi)容





    
    
    
    頁(yè)面 B


    

我是 B 頁(yè)面的內(nèi)容


七、使用 WebSocket 實(shí)現(xiàn)跨域

WebSocket 沒有跨域限制,高級(jí) API(不兼容),想要兼容低版本瀏覽器,可以使用 socket.io 的庫(kù),WebSocket 與 HTTP 內(nèi)部都是基于 TCP 協(xié)議,區(qū)別在于 HTTP 是單向的(單雙工),WebSocket 是雙向的(全雙工),協(xié)議是 ws://wss:// 對(duì)應(yīng) http://https://,因?yàn)闆]有跨域限制,所以使用 file:// 協(xié)議也可以進(jìn)行通信。

由于我們?cè)?NodeJS 服務(wù)中使用了 WebSocket,所以需要安裝對(duì)應(yīng)的依賴:

npm install ws --save




    
    頁(yè)面


    

const express = require("express");
let app = express();

// 引入 webSocket
const WebSocket = require("ws");
// 創(chuàng)建連接,端口號(hào)與前端相對(duì)應(yīng)
let wss = new WebSocket.Server({ port: 3000 });

// 監(jiān)聽連接
wss.on("connection", function(ws) {
    // 監(jiān)聽消息
    ws.on("message", function(data) {
        // 打印消息
        console.log(data); // I love you
        // 發(fā)送消息
        ws.send("I love you, too");
    });
});


八、使用 nginx 實(shí)現(xiàn)跨域

nginx 本身就是一個(gè)服務(wù)器,因此我們需要去 nginx 官網(wǎng)下載服務(wù)環(huán)境 http://nginx.org/en/download....。

下載后解壓到一個(gè)文件夾中

雙擊 nginx.exe 啟動(dòng)(此時(shí)可以通過 http://localhost 訪問 nginx 服務(wù))

在目錄新建 json 文件夾

進(jìn)入 json 文件夾新建 data.json 文件并寫入內(nèi)容

回到 nginx 根目錄進(jìn)入 conf 文件夾

使用編輯器打開 nginx.conf 進(jìn)行配置

data.json 文件:

{
    "name": "nginx"
}

nginx.conf 文件:

server {
    .
    .
    .
    location ~.*.json {
        root json;
        add_header "Access-Control-Allow-Origin" "*";
    }
    .
    .
    .
}

含義:

~.*.json:代表忽略大小寫,后綴名為 json 的文件;

root json:代表 json 文件夾;

add_header:代表加入跨域的響應(yīng)頭及允許訪問的域,* 為允許任何訪問。

nginx 根目錄啟動(dòng) cmd 命令行(windows 系統(tǒng)必須使用 cmd 命令行)執(zhí)行下面代碼重啟 nginx

nginx -s reload

不跨域訪問:http://localhost/data.json

跨域訪問時(shí)需要?jiǎng)?chuàng)建跨域條件代碼如下:

// 服務(wù)器
const express = require("express");
let app = express();

app.use(express.static(__dirname));
app.listen(3000);

跨域訪問:http://localhost:3000/index.html





    
    nginx跨域


    


九、使用 http-proxy-middleware 實(shí)現(xiàn)跨域

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

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




    
    proxy 跨域


    

中間代理服務(wù)中使用了 http-proxy-middleware 中間件,因此需要提前下載:

npm install http-proxy-middleware --save-dev
// 中間代理服務(wù)器
const express = require("express");
let proxy = require("http-proxy-middleware");
let app = express();

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

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

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

app.listen(3000);
// 服務(wù)器
const http = require("http");
const qs = require("querystring");

const server = http.createServer();

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

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

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

server.listen("8080");
2、vue 框架的跨域(1 次跨域)

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

// 導(dǎo)出服務(wù)器配置
module.exports = {
    entry: {},
    module: {},
    ...
    devServer: {
        historyApiFallback: true,
        proxy: [{
            context: "/login",
            target: "http://www.proxy2.com:8080",  // 代理跨域目標(biāo)接口
            changeOrigin: true,
            secure: false,  // 當(dāng)代理某些 https 服務(wù)報(bào)錯(cuò)時(shí)用
            cookieDomainRewrite: "www.domain1.com"  // 可以為 false,表示不修改
        }],
        noInfo: true
    }
}


本篇文章在于幫助我們理解跨域,以及不同跨域方式的基本原理,在公司的項(xiàng)目比較多,多個(gè)域使用同一個(gè)服務(wù)器或者數(shù)據(jù),以及在開發(fā)環(huán)境時(shí),跨域的情況基本無法避免,一般會(huì)有各種各樣形式的跨域解決方案,但其根本原理基本都在上面的跨域方式當(dāng)中方式,我們可以根據(jù)開發(fā)場(chǎng)景不同,選擇最合適的跨域解決方案。


文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/98284.html

相關(guān)文章

  • 九種跨域方式實(shí)現(xiàn)原理(完整版)

    摘要:二跨域解決方案原理利用標(biāo)簽沒有跨域限制的漏洞,網(wǎng)頁(yè)可以得到從其他來源動(dòng)態(tài)產(chǎn)生的數(shù)據(jù)。使用反向代理實(shí)現(xiàn)跨域,是最簡(jiǎn)單的跨域方式。 前言 前后端數(shù)據(jù)交互經(jīng)常會(huì)碰到請(qǐng)求跨域,什么是跨域,以及有哪幾種跨域方式,這是本文要探討的內(nèi)容。 本文完整的源代碼請(qǐng)猛戳github博客,紙上得來終覺淺,建議動(dòng)手敲敲代碼 一、什么是跨域? 1.什么是同源策略及其限制內(nèi)容? 同源策略是一種約定,它是瀏覽器最核心...

    edgardeng 評(píng)論0 收藏0
  • 九種跨域方式實(shí)現(xiàn)原理(完整版)

    摘要:二跨域解決方案原理利用標(biāo)簽沒有跨域限制的漏洞,網(wǎng)頁(yè)可以得到從其他來源動(dòng)態(tài)產(chǎn)生的數(shù)據(jù)。使用反向代理實(shí)現(xiàn)跨域,是最簡(jiǎn)單的跨域方式。 前言 前后端數(shù)據(jù)交互經(jīng)常會(huì)碰到請(qǐng)求跨域,什么是跨域,以及有哪幾種跨域方式,這是本文要探討的內(nèi)容。 本文完整的源代碼請(qǐng)猛戳github博客,紙上得來終覺淺,建議動(dòng)手敲敲代碼 一、什么是跨域? 1.什么是同源策略及其限制內(nèi)容? 同源策略是一種約定,它是瀏覽器最核心...

    justCoding 評(píng)論0 收藏0
  • 優(yōu)秀文章收藏(慢慢消化)持續(xù)更新~

    摘要:整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí)原文協(xié)作規(guī)范中文技術(shù)文檔協(xié)作規(guī)范阮一峰編程風(fēng)格凹凸實(shí)驗(yàn)室前端代碼規(guī)范風(fēng)格指南這一次,徹底弄懂執(zhí)行機(jī)制一次弄懂徹底解決此類面試問題瀏覽器與的事件循環(huán)有何區(qū)別筆試題事件循環(huán)機(jī)制異步編程理解的異步 better-learning 整理收藏一些優(yōu)秀的文章及大佬博客留著慢慢學(xué)習(xí) 原文:https://www.ahwgs.cn/youxiuwenzhan...

    JeOam 評(píng)論0 收藏0
  • HTML5 安全問題解析

    摘要:本地安全問題在之前引入了本地這個(gè)東西,但是后面被廢除了,他的安全點(diǎn)和后臺(tái)數(shù)據(jù)庫(kù)的關(guān)注點(diǎn)差不多,就是要防止在數(shù)據(jù)中混入查詢指令。僵尸網(wǎng)絡(luò)風(fēng)險(xiǎn)中解決了單線程問題,提出了機(jī)制,它為提供多線程支持,但是多線程帶來了一個(gè)非常可怕的危險(xiǎn)僵尸網(wǎng)絡(luò)。 HTML5 安全問題解析 標(biāo)簽: html html5 web安全 本文參考: w3school:html5相關(guān)基礎(chǔ)知識(shí)(w3school.com.c...

    maybe_009 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<