超碰人人人人人,亚洲AV午夜福利精品一区二区,亚洲欧美综合区丁香五月1区,日韩欧美亚洲系列

LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

跨域問(wèn)題?同源策略大全

liguoquan
2024年12月27日 12:42 本文熱度 1042
:跨域問(wèn)題?同源策略大全


前言:跨域與同源策略

跨域:通常出現(xiàn)在Web開發(fā)中,特別是在涉及到Ajax請(qǐng)求或Fetch API調(diào)用時(shí),當(dāng)一個(gè)網(wǎng)頁(yè)嘗試從不同的源加載資源時(shí),就會(huì)遇到跨域問(wèn)題。這里所說(shuō)的“不同的源”,是指請(qǐng)求資源的源(由協(xié)議、域名和端口號(hào)組成)與提供資源的源不一致。

http://  192.168.3.1  :3000  /home

協(xié)議(http),         域名(192.168.3.1),      端口(3000),   路徑(/home)

同源策略:是為了保護(hù)用戶的隱私和數(shù)據(jù)安全,如果沒(méi)有同源策略,惡意網(wǎng)站可以通過(guò)腳本非法獲取其他網(wǎng)站上的敏感數(shù)據(jù),所以瀏覽器會(huì)通過(guò)實(shí)施同源策略來(lái)限制不同源之間的直接通信。同時(shí),也有些特別的情況是不受同源策略限制的,比如:

img標(biāo)簽下的

link標(biāo)簽下的

script標(biāo)簽下的

一:JSONP實(shí)現(xiàn)同源

  1. 借助script標(biāo)簽的src屬性不受同源策略的影響來(lái)發(fā)送請(qǐng)求

  2. 給后端攜帶一個(gè)參數(shù) callback 并在前端定義 callback 函數(shù)體

  3. 后端返回 callback 的調(diào)用形式并將要響應(yīng)的值作為 callback 函數(shù)的參數(shù)

  4. 當(dāng)瀏覽器接收到響應(yīng)后,就會(huì)觸發(fā)全局的 callback 函數(shù),從而讓 callback 以參數(shù)的形式接收后端的響應(yīng)

前端代碼

js
代碼解讀
復(fù)制代碼
<script>    function jsonp(url, cb) {      return new Promise((resolve, reject) => {        const script = document.createElement('script');        // 注冊(cè)一個(gè)全局的回調(diào)函數(shù)        window[cb] = function(data) {          resolve(data);        };        // 設(shè)置腳本標(biāo)簽的src屬性為請(qǐng)求的URL,并附加一個(gè)回調(diào)參數(shù)        script.src = `${url}?cb=${cb}`;        // 將腳本標(biāo)簽添加到body中,觸發(fā)異步請(qǐng)求        document.body.appendChild(script);      });    }    // 使用jsonp函數(shù)發(fā)起請(qǐng)求    jsonp('http://localhost:3000', 'callback').then(res => {      console.log(res);  // 在控制臺(tái)輸出結(jié)果    }); </script>

后端代碼

js
代碼解讀
復(fù)制代碼
const http = require('http'); // 創(chuàng)建一個(gè)HTTP服務(wù)器實(shí)例,并指定一個(gè)處理請(qǐng)求的回調(diào)函數(shù) http.createServer(function(req, res) {  // 解析請(qǐng)求的URL,并獲取查詢參數(shù)  const query = new URL(req.url, `http://${req.headers.host}`).searchParams;  // 檢查查詢參數(shù)中是否包含'cb'(callback)  if (query.get('cb')) {    // 獲取回調(diào)函數(shù)名    const cb = query.get('cb'); // 例如:'callback'    // 準(zhǔn)備要返回的數(shù)據(jù)    const data = 'hello world';    // 構(gòu)造JSONP格式的字符串,格式為:callback("hello world")    const result = `${cb}("${data}")`;    res.end(result);  } }).listen(3000);

$

但使用這種方法實(shí)現(xiàn)同源有兩個(gè)缺點(diǎn):

  • 需要后端配合
  • 只支持 get 請(qǐng)求

二:cors實(shí)現(xiàn)同源

核心思想是后端通過(guò)Access-Control-Allow-Origin設(shè)置響應(yīng)頭來(lái)指定允許的域名,以此來(lái)通知瀏覽器此時(shí)同源策略不生效

前端代碼

js
代碼解讀
復(fù)制代碼
   <script>        const xhr = new XMLHttpRequest();        xhr.open('GET', 'http://localhost:3000');        xhr.send();        xhr.onreadystatechange = function () {            if (xhr.readyState == 4 && xhr.status == 200) {                console.log(xhr.responseText);        }    }    </script>

后端代碼

js
代碼解讀
復(fù)制代碼
const http = require('http'); // 創(chuàng)建一個(gè)HTTP服務(wù)器實(shí)例,并指定一個(gè)處理請(qǐng)求的回調(diào)函數(shù) http.createServer((req, res) => {   // 設(shè)置響應(yīng)頭的狀態(tài)碼為200,表示成功   res.writeHead(200, {       // 設(shè)置Access-Control-Allow-Origin響應(yīng)頭,允許來(lái)自'http://127.0.0.1:5500'的請(qǐng)求       'Access-Control-Allow-Origin': 'http://127.0.0.1:5500',   });   res.end('Hello World'); }).listen(3000);

同樣,也可以設(shè)置Access-Control-Allow-methods來(lái)設(shè)置相應(yīng)的請(qǐng)求方法,post,get等等

三:proxy代理

  1. 前端應(yīng)用將原本需要跨域訪問(wèn)的請(qǐng)求發(fā)送給自身的后端服務(wù)器
  2. 后端服務(wù)器再將請(qǐng)求轉(zhuǎn)發(fā)至實(shí)際的目標(biāo)服務(wù)器,并從目標(biāo)服務(wù)器獲取數(shù)據(jù)
  3. 最后將數(shù)據(jù)返回給前端應(yīng)用。

這樣通過(guò)后端服務(wù)器作為中間層代理轉(zhuǎn)發(fā)請(qǐng)求,可以繞過(guò)瀏覽器同源策略的限制,實(shí)現(xiàn)跨域數(shù)據(jù)的獲取。

實(shí)現(xiàn)過(guò)程:

  • 創(chuàng)建一個(gè)XMLHttpRequest對(duì)象并發(fā)送一個(gè)GET請(qǐng)求到后端(http://192.168.1.63:3000)。onreadystatechange事件處理器會(huì)在請(qǐng)求狀態(tài)改變時(shí)觸發(fā),并在請(qǐng)求完成且響應(yīng)狀態(tài)碼為200 OK時(shí)打印出響應(yīng)文本。
js
代碼解讀
復(fù)制代碼
   <script>        const xhr = new XMLHttpRequest();        xhr.open('GET', 'http://192.168.1.63:3000');        xhr.send();        xhr.onreadystatechange = function () {            if (xhr.readyState == 4 && xhr.status == 200) {                console.log(xhr.responseText);        }    }    </script>
  • 后端創(chuàng)建一個(gè)簡(jiǎn)單的HTTP服務(wù)器,監(jiān)聽(tīng)3000端口,設(shè)置響應(yīng)頭Access-Control-Allow-Origin*,允許任何來(lái)源都可以訪問(wèn)此資源,解決跨域問(wèn)題。
  • 再創(chuàng)建一個(gè)新的HTTP請(qǐng)求到目標(biāo)服務(wù)器192.168.1.63:3000,與前端設(shè)置的要一致,并將從目標(biāo)服務(wù)器收到的數(shù)據(jù)轉(zhuǎn)發(fā)回原始請(qǐng)求者。
js
代碼解讀
復(fù)制代碼
const http = require('http'); http.createServer(function(req, res) {  // 設(shè)置響應(yīng)頭允許任何來(lái)源訪問(wèn)此資源  res.writeHead(200, {    "access-control-allow-origin": "*"  });  // 創(chuàng)建一個(gè)新的請(qǐng)求到目標(biāo)服務(wù)器  const options = {    host: '192.168.1.63',    port: '3000',    path: '/',    method: 'GET',    headers: {}  };  // 發(fā)起代理請(qǐng)求  http.request(options, proxyRes => {    // 當(dāng)從目標(biāo)服務(wù)器接收到數(shù)據(jù)時(shí),轉(zhuǎn)發(fā)給原始請(qǐng)求者    proxyRes.on('data', function(data) {      res.end(data.toString())    });  }).end(); // 結(jié)束代理請(qǐng)求 }).listen(3000); // 監(jiān)聽(tīng)3000端口

請(qǐng)注意,這樣的代理服務(wù)器僅適用于開發(fā)環(huán)境,在生產(chǎn)環(huán)境中應(yīng)當(dāng)謹(jǐn)慎使用,因?yàn)樗赡軒?lái)安全風(fēng)險(xiǎn),如中間人攻擊等

四:nginx實(shí)現(xiàn)同源

相當(dāng)于node代理,大致原理如下:

五:Websocket實(shí)現(xiàn)同源

  • websocket是http協(xié)議的一部分,所以它有同源策略
  • websocket是長(zhǎng)連接,可以發(fā)送和接收消息
  • websocket是html5新增的協(xié)議,它是一種雙向通信協(xié)議,建立在tcp之上

實(shí)現(xiàn)過(guò)程: 前端

  • 創(chuàng)建一個(gè)新的 WebSocket 實(shí)例,并傳入 url 參數(shù)。
  • 設(shè)置 onopen 事件處理器,當(dāng) WebSocket 連接成功打開時(shí),將 params 對(duì)象轉(zhuǎn)換為 JSON 字符串并通過(guò) WebSocket 發(fā)送。
  • 設(shè)置 onmessage 事件處理器,當(dāng)從 WebSocket 接收到消息時(shí),解析接收到的數(shù)據(jù),并調(diào)用 resolve 方法,將解析后的數(shù)據(jù)作為 Promise 的結(jié)果返回。
  • 調(diào)用 myWebSocket 函數(shù),傳入 WebSocket 服務(wù)器的 URL  和一個(gè)包含對(duì)象并使用 .then 方法處理 Promise 的解決情況
js
代碼解讀
復(fù)制代碼
 <script>    function myWebSocket(url, params = {}) {      return new Promise(function(resolve, reject) {        //創(chuàng)建一個(gè)新的 `WebSocket` 實(shí)例        const socket = new WebSocket(url)                //將 `params` 對(duì)象轉(zhuǎn)換為 JSON 字符串并通過(guò) WebSocket 發(fā)送。        socket.onopen = () => {          socket.send(JSON.stringify(params))        }                //解析接收到的數(shù)據(jù),并作為 `Promise` 的結(jié)果返回        socket.onmessage = function(e) {          resolve(e.data);        }      })    }    myWebSocket('ws://localhost:3000', {age: 18}).then(res => {      console.log(res);      })  </script>

后端

  • npm init -y 初始化為后端項(xiàng)目
  • npm ws 安裝ws
  • 使用 ws 庫(kù)來(lái)創(chuàng)建一個(gè) WebSocket 服務(wù)器,并監(jiān)聽(tīng)3000端口。
  • 監(jiān)聽(tīng) 'connection' 事件,每當(dāng)有一個(gè)新的客戶端連接到 WebSocket 服務(wù)器時(shí),就會(huì)觸發(fā)此事件處理器。
  • 為每個(gè)連接注冊(cè)一個(gè) 'message' 事件處理器,當(dāng)從客戶端接收到消息時(shí)觸發(fā)。并設(shè)置一個(gè)定時(shí)器,每?jī)擅胝{(diào)用一次
js
代碼解讀
復(fù)制代碼
const WebSocket = require('ws'); // 創(chuàng)建一個(gè) WebSocket 服務(wù)器實(shí)例,并監(jiān)聽(tīng)3000端口 const ws = new WebSocket.Server({ port: 3000 }); // 監(jiān)聽(tīng) 'connection' 事件,每當(dāng)有新的客戶端連接到服務(wù)器時(shí)觸發(fā) ws.on('connection', function(obj) {    // 監(jiān)聽(tīng) 'message' 事件,每當(dāng)從客戶端接收到消息時(shí)觸發(fā)    obj.on('message', function(data) {        // 向客戶端發(fā)送一條歡迎消息        obj.send('歡迎訪問(wèn)');        // 設(shè)置一個(gè)定時(shí)器,每隔2秒向客戶端發(fā)送一條消息        setInterval(() => {              obj.send();        }, 2000);    }); });

六:postMessage

當(dāng)頁(yè)面一通過(guò)iframe嵌套了頁(yè)面二,這兩個(gè)頁(yè)面因?yàn)榭缬驘o(wú)法進(jìn)行通訊,可以使用postMessage實(shí)現(xiàn)跨域通訊

postMessage 是一種在不同窗口、文檔或框架之間安全地進(jìn)行消息傳遞的方式,它支持跨源消息傳遞

下面帶友友們實(shí)操一下:

主頁(yè) (index.html)

  • 初始化 obj 對(duì)象,包含姓名和年齡信息。
  • 當(dāng) <iframe> 加載完成后,通過(guò) postMessage 發(fā)送 obj 對(duì)象給 detail.html。
  • index.html 的 onmessage 事件處理器接收到 detail.html 發(fā)送的回復(fù)消息。
js
代碼解讀
復(fù)制代碼
<body>    <h2>首頁(yè)</h2>    <iframe id="frame" src="http://127.0.0.1:5500/postMessage/detail.html" width="800" height="500" frameborder="0"></iframe>    <script>        // 定義一個(gè)對(duì)象,包含要傳遞的數(shù)據(jù)        let obj = {name: 'midsummer', age: 18};        // 當(dāng) iframe 加載完成后執(zhí)行以下代碼        document.getElementById('frame').onload = function() {            // 向 iframe 中的頁(yè)面發(fā)送一個(gè)消息            this.contentWindow.postMessage(obj, 'http://127.0.0.1:8080');            // 設(shè)置全局的 onmessage 事件處理器,用于接收來(lái)自其他窗口的消息            window.onmessage = function(e) {                console.log(e.data); // 打印接收到的消息數(shù)據(jù)            };        };    </script> </body>

詳情頁(yè) (detail.html)

  • 接收到來(lái)自 index.html 的消息。
  • 解析消息數(shù)據(jù),并更新頁(yè)面上的顯示內(nèi)容。
  • 向 index.html 發(fā)送回復(fù)消息。
js
代碼解讀
復(fù)制代碼
<body>    <h3>詳情頁(yè) -- <span id="title"></span> </h3>    <script>        // 獲取頁(yè)面中的 span 元素,用于顯示信息        let title = document.getElementById('title');        // 設(shè)置全局的 onmessage 事件處理器,用于接收來(lái)自其他窗口的消息        window.onmessage = function (e) {            // 解構(gòu)賦值,提取消息中的 data 屬性,以及消息來(lái)源的 origin 屬性            let { data: {name, age}, origin } = e;            // 更新 span 元素的文本內(nèi)容,顯示發(fā)送過(guò)來(lái)的名字和年齡信息            title.innerText = `${name} ${age}`;            // 向消息來(lái)源(即發(fā)送消息的窗口)發(fā)送新的消息            e.source.postMessage(`midsummer現(xiàn)在${++age}歲`, origin);        };    </script> </body>

簡(jiǎn)單來(lái)說(shuō)就是通過(guò)設(shè)置 postMessage 的第二個(gè)參數(shù)為目標(biāo)源地址,可以限制消息只能發(fā)送給指定源的窗口。當(dāng)一個(gè)窗口接收到消息時(shí),它可以通過(guò) onmessage 事件處理器來(lái)處理消息,并且可以使用 e.source 來(lái)回發(fā)消息給發(fā)送方。

七:document.domain

當(dāng)兩個(gè)頁(yè)面通過(guò)iframe進(jìn)行嵌套,且兩個(gè)頁(yè)面的二級(jí)域名一致,可以使用document.domain實(shí)現(xiàn)同源 ,不知道二級(jí)域名的友友們可以參考下圖

主頁(yè):

js
代碼解讀
復(fù)制代碼
<body>    <h2>首頁(yè)</h2>    <iframe id="frame" src="http://127.0.0.1:5500/postMessage/detail.html" width="800" height="500" frameborder="0"></iframe>    <script>        // 設(shè)置當(dāng)前頁(yè)面的 document.domain 為 '127.0.0.1'        document.domain = '127.0.0.1';       // 當(dāng) iframe 加載完成時(shí)執(zhí)行的函數(shù)        document.getElementById('frame').onload = function() {       // 輸出 contentWindow 中的 data 變量        console.log(this.contentWindow.data);      };    </script> </body>

詳情頁(yè):

js
代碼解讀
復(fù)制代碼
<script> document.domain = '127.0.0.1' var data = 'domain' </script>

通過(guò)設(shè)置 document.domain放寬限制,允許在同一個(gè)頂級(jí)域名下的不同子域名之間進(jìn)行通信。在 index.html 和 detail.html 中都設(shè)置了 document.domain 為 '127.0.0.1',確保 index.html 和 detail.html 之間的 document.domain 是相同的,從而繞過(guò)了同源策略的限制。


作者:midsummer18
鏈接:https://juejin.cn/post/7411168461500366860
來(lái)源:稀土掘金
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。

該文章在 2024/12/27 12:42:27 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved