最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

全解跨域請求問題處理方法及分析

 更新時間:2023年07月20日 11:22:31   作者:Faremax  
這篇文章主要為大家介紹了全解跨域請求問題處理方法及分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>

為什么會有跨域問題

我們試想一下以下幾種情況:

  • 我們打開了一個天貓并且登錄了自己的賬號,這時我們再打開一個天貓的商品,我們不需要再進行一次登錄就可以直接購買商品,因為這兩個網頁是同源的,可以共享登錄相關的 cookie 或 localStorage 數據;
  • 如果你正在用支付寶或者網銀,同時打開了一個不知名的網頁,如果這個網頁可以訪問你支付寶或者網銀頁面的信息,就會產生嚴重的安全的問題。如果該未知網站是黑客的工具,那他就可以借此發(fā)起 CSRF 攻擊了。顯然瀏覽器不允許這樣的事情發(fā)生;
  • 想必你也有過同時登陸好幾個 qq 賬號的情況,如果同時打開各自的 qq 空間瀏覽器會有一個小號模式,也就是另外再打開一個窗口專門用來打開第二個 qq 賬號的空間。

為了解決不同域名相互訪問數據導致的不安全問題,Netscape提出的一個著名的安全策略——同源策略,它是指同一個“源頭”的數據可以自由訪問,但不同源的數據相互之間都不能訪問。

同源策略

很明顯,上述第1個和第3個例子中,不同的天貓商店和 qq 空間屬于同源,可以共享登錄信息。qq 為了區(qū)別不同的 qq 的登錄信息,重新打開了一個窗口,因為瀏覽器的不同窗口是不能共享信息的。而第2個例子中的支付寶、網銀、不知名網站之間是非同源的,所以彼此之間無法訪問信息,如果你執(zhí)意想請求數據,會提示異常:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

那么什么是同源的請求呢?同源請求要求被請求資源頁面和發(fā)出請求頁面滿足3個相同:

協(xié)議相同
host相同
端口相同

簡單理解一下:

/*以下兩個數據非同源,因為協(xié)議不同*/
http://www.abc123.com.cn/item/a.js
https://www.abc123.com.cn/item/a.js
/*以下兩個數據非同源,因為域名不同*/
http://www.abc123.com.cn/item/a.js
http://www.abc123.com/item/a.js
/*以下兩個數據非同源,因為主機名不同*/
http://www.abc123.com.cn/item/a.js
http://item.abc123.com.cn/item/a.js
/*以下兩個數據非同源,因為協(xié)議不同*/
http://www.abc123.com.cn/item/a.js
http://www.abc123.com.cn:8080/item/a.js
/* 以下兩個數據非同源,域名和 ip 視為不同源
 * 這里應注意,ip和域名替換一樣不是同源的
 * 假設www.abc123.com.cn解析后的 ip 是 195.155.200.134
 */
http://www.abc123.com.cn/
http://195.155.200.134/
/*以下兩個數據同源*/                               /* 這個是同源的*/
http://www.abc123.com.cn/source/a.html
http://www.abc123.com.cn/item/b.js

HTTP 簡單請求和非簡單請求

http 請求滿足一下條件時稱為簡單請求,否則是非簡單請求:

  • 請求方法是 HEAD,GET,POST 之一
  • HTTP的頭信息不超出以下幾種字段:

    • Accept
    • Accept-Language
    • Content-Language
    • Last-Event-ID
    • Content-Type

Content-Type 取值僅限于 application/x-www-form-urlencodedmultipart/form-datatext/plain

非簡單請求在發(fā)送之前會發(fā)送一次 OPTION 預請求,如果在跨域操作遇到返回 405(Method Not Allowed) 錯誤,需要服務端允許 OPTION 請求。

HTTP 跨域訪問的處理辦法及適用條件

JSOP

適用條件:請求的 GET 接口需要支持 jsonp 訪問

這里需要強調的是,jsonp 不屬于 Ajax 的部分,它只是把 url 放入 script 標簽中實現的數據傳輸,不受同源策略限制。由于一般庫也會把它和 Ajax 封裝在一起,由于其和 Ajax 根部不是一回事,所以這里不討論。下面是一個 jsonp 的例子:

window.jsonpCallback = console.log;
var JSONP = document.createElement("script");
JSONP.src = "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=13122222222&t=" + Math.random() + "&callback=jsonpCallback";;
document.body.appendChild(JSONP);

后端支持jsonp方式(Nodejs)

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

server.on('request', function(req, res) {
    var params = qs.parse(req.url.split('?')[1]);
    var fn = params.callback;

    // jsonp返回設置
    res.writeHead(200, { 'Content-Type': 'text/javascript' });
    res.write(fn + '(' + JSON.stringify(params) + ')');

    res.end();
});

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

document.domain

適用條件: host 中僅服務器不同的情況,域名本身應該相同

www.dom.com 和 w1.dom.com 需要同源才能訪問,可以將 document.domain 設置為 dom.com 解決該問題

document.domain = 'dom.com';

例如,我想開發(fā)一個瀏覽器插件,發(fā)現騰訊視頻頁有個 iframe 其本身的跨域的,無法獲取其 iframe 的 DOM 對象。但域名部分相同,可以通過該方法解決.

注:如果你想設置它為完全不同的域名,那肯定會報同源錯誤的,注意使用范圍!

嵌入 iframe

適用條件: host 中僅服務器不同的情況,域名本身應該相同

有了上面的例子就不難理解這個方法了,嚴格來說這不是一個新的方法,而是上一個方法的延伸。通過設置document.domain, 使同一個域名下不同服務器名的頁面可以訪問數據,但值得注意的是:這個數據訪問不是相互的,外部頁面可以訪問 iframe 內部的數據,但 iframe 無法不能訪問外部的數據。

location.hash

適用條件:iframe 和其宿主頁面通信

一個完成的 url 中 # 及后面的部分為 hash, 可以通過修改這個部分完成iframe 的和宿主直接的數據傳遞,下面演示一下 iframe 頁面(B.html)像宿主(A.html)傳數據, 反之同理:

// A.html
data = ['book', 'map', 'shelf', 'knife'];
setTimeout(() => {
  location.hash = window.encodeURIComponent(data.join('/'));
}, 1000);
// B.html
window.parent.onhashchange = function (e) {
  var data = window.decodeURIComponent(e.newURL.split('#')[1]).split('/');
  console.log(data);  // ["book", "map", "shelf", "knife"]
}

*注意反向傳遞數據時應該使用 window.parent.location.hash

window.name

適用條件:宿主頁面和 iframe 之間通信

window對象有個name屬性,該屬性有個特征:即在 window 的生命周期內,窗口載入的所有的頁面 (iframe) 都是共享一個 window.name 的,每個頁面對 window.name 都有讀寫的權限,window.name 是持久存在一個窗口載入過的所有頁面中的,并不會因新頁面的載入而進行重置。

這樣在 window 中編輯 window.name 就可以在 iframe 中得到,但這個過程缺乏監(jiān)聽,宿主頁面(A.html)和 iframe 頁面(B.html)相互并不知道對方在什么時候修改該值:

// A.html
setTimeout(() => {
  window.parent.name = "what!";
}, 2000);
// B.html
setTimeout(() => {
  console.log(window.name);   // what!
}, 2500);

postMessage

適用條件:postMessage 是 H5 提出的一個消息互通的機制,解決 iframe 不能消息互通的問題,也可以跨 window 通信,語法如下:

// 在 www.siteA.com 中發(fā)出消息
// @message{any} 要發(fā)送的數據(注意:老版本瀏覽器只支持字符串類型)
// @targetOrigin{string} 規(guī)定接收數據的域,只有其指定的域才能收到消息,如果為"*"則沒用域的限制
// transfer{any} 與 message 一同發(fā)送并轉移所有權
window.postMessage(message, targetOrigin, [transfer]);
// 在另一個頁面接受參數
window.onmessage = console.log;

這里暫不談論第三個參數,因為你可能一輩子也用不到它。而 targetOrigin 最好不要使用 "*",除非你想讓所有頁面都收到你的消息。

一種你會用到的場景(iframe):

<!-- www.siteA.com/index.html -->
<script>
    window.addEventListener('message', function(e){
        console.log('Get message: "' + e.data.title + '" from ' + e.origin);  // 'Get message: "Saying hello to siteA!" from http://www.siteB.com'
    });
</script>
<iframe src="http://www.siteB.com"></iframe>
<!-- www.siteB.com/index.html -->
<script>
    function sendMessage(){
        window.postMessage({title: 'Saying hello to siteA!'}, 'http://www.siteA.com');
    }
    setTimeout(sendMessage, 2000);
</script>

這一種僅僅是沒有了iframe,當你在同一個瀏覽器窗口同時打開 www.siteA.com 和 www.siteB.com 兩個標簽時也可以這樣用

<!-- www.siteA.com/index.html -->
<script>
    window.addEventListener('message', function(e){
        console.log('Get message: "' + e.data.title + '" from ' + e.origin);  // 'Get message: "Saying hello to siteA!" from http://www.siteB.com'
    });
</script>
<!-- www.siteB.com/index.html -->
<script>
    function sendMessage(){
        window.postMessage({title: 'Saying hello to siteA!'}, 'http://www.siteA.com');
    }
    setTimeout(sendMessage, 2000);
</script>

反向代理服務器

頁面需要訪問一些跨域接口,由于代理的存在,在服務器看來請求是不跨域,所以使用各種請求。但需要注意 http 到 https 的兼容問題。

比如當我在一些在線平臺開發(fā)網站后得到一個頁面 www.site-A.com, 而這個頁面需要請求我自己的數據服務器data.site-B.com上的數據, 這樣同樣會產生跨域問題,但是www.site-A.com這個頁面是掛在第三方服務器上的,解決這個問題可以采用代理服務器的方法:

var express = require('express');
var request = require('request');
var app = express();

app.use('/api', function(req, res) {
  var url = 'http://data.site-B.com/api2' + req.url;
  req.pipe(request(url)).pipe(res);
});
app.use('/', function(req, res) {
  var url = 'http://data.site-C.com';
  req.pipe(request(url)).pipe(res);
});

當然還需要同時配置一個 host:

127.0.0.1 local.www.site-B.com

然后訪問 local.www.site-B.com 就 OK 了。

CORS

適用條件:CORS 需要服務端支持,且存在一定的兼容性問題(如今你已經可以不考慮,但必要時不要忘了這個'bug')。其通過添加 http 頭關鍵字實現跨域可訪問,包括如下頭內容:

# www.siteA.com/api 返回相應需要具有如下 http 頭字段
Access-Control-Allow-Origin: 'http://www.siteB.com'    # 指定域可以請求,通配符'*'(必須)
Access-Control-Allow-Methods: 'GET,PUT,POST,DELETE'    # 指定允許的跨域請求方式(必須)
Access-Control-Allow-Headers: 'Content-Type'           # 請求中必須包含的 http 頭字段
Access-Control-Allow-Credentials: true                 # 配合請求中的 withCredentials 頭進行請求驗證

通過 express 實現也很簡單,在注冊路由之前添加:

var cors = require('cors');   // 通過 npm 安裝
app.use(cors());

當然你也可以自定義一個中間件:

// 自定義中間件
var cors = function (req, res, next) {
 // 自定義設置跨域需要的響應頭。
 res.header('Access-Control-Allow-Origin', 'http://www.siteB.com');
 res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
 next();
};

app.use(cors); // 運用跨域的中間件

WebSocket 協(xié)議跨域

ws 協(xié)議是 H5 中的 web 全雙工通信解決方案,常規(guī) http 屬于請求相應的過程,在客戶端沒有請求的情況下,服務端無法給客戶端主動推送數據,ws 協(xié)議解決了這個問題,但處于安全考慮,其同樣有同源策略的限制。

*這里不討論通過長連接和服務端掛起請求等方法推送數據,本文只討論跨域。

下面舉個例子(依賴socket.io.js):

// 前端部分
socket.on('connect', function() {
  // 監(jiān)聽服務端消息
  socket.on('message', function(msg) {
    console.log('data from server: ' + msg);
  });
  // 監(jiān)聽服務端關閉
  socket.on('disconnect', function() {
    console.log('Server socket has closed.');
  });
});
document.getElementById('input').onkeyup = function(e) {
  if(!e.shiftKey && !e.ctrlKey && !e.altKey && e.keyCode === 13)
    socket.send(this.value);
};
// 后端部分(node.js)
var http = require('http');
var socket = require('socket.io');
// 啟http服務
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)聽socket連接
socket.listen(server).on('connection', function(client) {
  // 監(jiān)聽客戶端信息
  client.on('message', function(msg) {
    client.send('hello:' + msg);
    console.log('data from client: ' + msg);
  });
  // 監(jiān)聽客戶端斷開
  client.on('disconnect', function() {
    console.log('Client socket has closed.');
  });
});

HTML 標簽中的 crossorigin 屬性

HTML 中 <img><video> 和 <script> 具有 crossorigin 屬性。添加屬性會使相應添加 CORS 相關 http 頭(需要服務器支持)。同時,其還有以下可能的取值:

  • user-credentials 該請求通過 cookie 交換 user-credentials,服務器相應需添加 Access-Control-Allow-Origin
  • anonymous 該請求不會通過 cookie 交換 user-credentials,服務器相應需添加 Access-Control-Allow-Credentials

當只寫了 crossorigin 屬性沒有指定值時,其默認值為 "anonymous"。即以下兩行代碼等價:

<scirpt src="a.com/vendor.js" corssorigin></script>
<scirpt src="a.com/vendor.js" corssorigin="anonymous"></script>

幾種不同的跨域方法比較

擴展:基于 webpack 的反向代理配置示例

添加 webpack 配置如下:

const config = {
  // ...
  devServer: {
    // ...
    proxy: {
      '/api': {
        target: 'https://data.site-B.com/api2',
        changeOrigin: true, // 允許跨域
        secure: false // 允許訪問 https
      },
      '/': {
        target: 'https://data.site-C.com',
        changeOrigin: true,
        secure: false
      },
    }
  }
};
module.exports = config;

擴展:基于 Nginx 反向代理和CORS配置示例

  • CORS 配置
location / {
  add_header  Access-Control-Allow-Origin *;
  add_header Access-Control-Allow-Credentials true;
  add_header  Access-Control-Allow-Methods: GET,PUT,POST,DELETE;
}
  • 反向代理配置
server {
    listen  7001;
    server_name  www.domain1.com;
    location / {
        proxy_pass   http://www.B.com:7001;  #反向代理
    }
}

以上就是全解跨域請求問題處理方法及分析的詳細內容,更多關于跨域請求問題處理的資料請關注腳本之家其它相關文章!

相關文章

最新評論

思茅市| 邢台县| 河北省| 庐江县| 荔浦县| 庐江县| 喀什市| 化州市| 桦南县| 克拉玛依市| 房山区| 会同县| 沙田区| 灌南县| 怀集县| 堆龙德庆县| 乌拉特后旗| 晴隆县| 承德市| 永安市| 信阳市| 仙桃市| 泸州市| 普定县| 太白县| 临潭县| 永清县| 云林县| 庆安县| 奉化市| 临猗县| 江华| 朝阳市| 赣榆县| 黄陵县| 嘉峪关市| 隆德县| 西畴县| 莱芜市| 华亭县| 建阳市|