溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

WEB前端跨域的示例分析

發(fā)布時間:2021-09-06 14:16:18 來源:億速云 閱讀:128 作者:小新 欄目:web開發(fā)

這篇文章主要為大家展示了“WEB前端跨域的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“WEB前端跨域的示例分析”這篇文章吧。


WEB前端跨域解決方案

跨域定義

廣義的定義:跨域是指一個域下的文檔或腳本試圖去請求另一個域下的資源。

1、資源跳轉(zhuǎn): 鏈接、重定向、表單提交

2、資源嵌入:<link>、<script><img/><frame>dom標簽,還有樣式中background:url()@font-face()等文件外鏈

3、腳本請求:js發(fā)起的ajax請求、domjs對象的跨域操作等

同源策略

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

同源策略限制以下幾種行為:

1、Cookie、LocalStorageIndexDB無法讀取

2、DOMJs對象無法獲得

3、AJAX請求不能發(fā)送

跨域解決方案

1)jsonp跨域

關(guān)于jsonp的原理把握一下幾點:

1、html標簽的src屬性沒有同源限制(支持跨域),瀏覽器解析script標簽時,會自動下載src屬性值(url)指向的資源;

2、script標簽指向的資源文件被下載后,其中的內(nèi)容會被立即執(zhí)行;

3、服務(wù)器端的程序會解析src屬性值中的url傳遞的參數(shù),根據(jù)這些參數(shù)針對性返回一個/多個函數(shù)調(diào)用表達式,這些函數(shù)調(diào)用表達式的參數(shù)就是客戶端跨域想得到的數(shù)據(jù);

4、服務(wù)器生成、返回的文件中,表達式調(diào)用的函數(shù)是已經(jīng)在本地提前定義好的,而參數(shù)就是希望從跨域服務(wù)器拿到的數(shù)據(jù)。字面的script標簽可以,動態(tài)添加到dom樹中的script也可以,后者更方便綁定事件。

5、只能實現(xiàn)get,也是他的弱點

實現(xiàn):

// 服務(wù)端返回:
test({
  code: 0,
  message: "成功",
});
<!-- 原生js -->
<script>
  var script = document.createElement("script");
  script.type = "text/javascript";
  // 傳參并指定回調(diào)執(zhí)行函數(shù)為callback
  script.src = "http://www.chuchur.com/login?callback=test";
  document.head.appendChild(script);
  // 回調(diào)執(zhí)行函數(shù)
  function test(res) {
    console.log(JSON.stringify(res));
  }
</script>
//jquery ajax:
$.ajax({
  url: "http://www.chuchur.com/login",
  type: "get",
  dataType: "jsonp", // 請求方式為jsonp
  jsonpCallback: "test", // 自定義回調(diào)函數(shù)名
  data: {},
});

//vue.js
this.$http
  .jsonp("http://www.chuchur.com/login", {
    params: {},
    jsonp: "test",
  })
  .then((res) => {
    console.log(res);
  });

2)document.domain + iframe 跨域

原理:這種方案只限于主域相同,子域不同的情況,其原理就是 兩個頁面通過js強制設(shè)置window.domain為主域,這樣就實現(xiàn)了同域。

實現(xiàn):

<!-- 父窗口 https://www.chuchur.com/a.html -->
<iframe id="iframe" src="https://b.chuchur.com/b.html"></iframe>
<script>
  document.domain = "chuchur.com";
  var user = "chuchur";
</script>
<!-- 子窗口 https://b.chuchur.com/b.html -->
<script>
  document.domain = "chuchur.com";
  // 獲取父窗口中變量
  alert("從父窗口取得數(shù)據(jù)" + window.parent.user);
</script>

3)location.hash + iframe跨域

原理: 其原理就是通過URL傳值,然后監(jiān)聽其hash值的變化,然后通過中間層做跳板,再利用父子窗口js parent最終來訪問同域所有頁面對象。

1: a.html ,域 2:b.html ,域 1:c.html 。

a.htmlb.html 不同域只能通過hash傳值通訊。

b.htmlc.html 也不同域也只能單項通訊

a.htmlc.html 同域,所以c.html可以通過parent來訪問a.html頁面對象

實現(xiàn)

1、a.html:(www.chuchur.com/a.html)

<iframe
  id="iframe"
  src="http://www.chuchur.org/b.html"
  style="display:none; "
></iframe>
<script>
  var iframe = document.getElementById("iframe");
  // 向b.html傳hash值
  setTimeout(function () {
    iframe.src = iframe.src + "#nick=chuchur";
  }, 1000);
  // 開放給同域c.html的回調(diào)方法
  function test(res) {
    alert("數(shù)據(jù)來自c.html ---> " + res);
  }
</script>
`

2、b.html:(www.chuchur.org/b.html)

<iframe
  id="iframe"
  src="http://www.chuchur.com/c.html"
  style="display:none; "
></iframe>
<script>
  var iframe = document.getElementById("iframe");
  // 監(jiān)聽a.html傳來的hash值,再傳給c.html
  window.onhashchange = function () {
    iframe.src = iframe.src + location.hash;
  };
</script>

3、c.html:(www.chuchur.com/c.html)

<script>
  // 監(jiān)聽b.html傳來的hash值
  window.onhashchange = function () {
    // 再通過操作同域a.html的js回調(diào),將結(jié)果傳回
    window.parent.parent.test("你好: " + location.hash.replace("#nick=", ""));
  };
</script>

4)window.name + iframe跨域

原理:利用window.name特有屬性,name值在不同的頁面甚至不同域 ,當頁面重新加載后依然存在,并且支持非常長的值,約 2MB。

實現(xiàn)

// 1.)a.html:(www.chuchur.com/a.html)
var proxy = function (url, callback) {
  var state = 0;
  var iframe = document.createElement("iframe");
  // 加載跨域頁面 ,先讓頁面的name執(zhí)行賦值,
  iframe.src = url;
  // onload事件會觸發(fā)2次,第1次加載跨域頁,并留存數(shù)據(jù)于window.name
  iframe.onload = function () {
    if (state === 1) {
      // 第2次onload(同域proxy頁)成功后,讀取同域window.name中數(shù)據(jù)
      test(iframe.contentWindow.name);
      destoryFrame();
    } else if (state === 0) {
      // 第1次onload(跨域頁)成功后,切換到同域代理頁面
      iframe.contentWindow.location = "http://www.chuchur.com/b.html";
      state = 1;
    }
  };
  document.body.appendChild(iframe);
  // 獲取數(shù)據(jù)以后銷毀這個iframe,釋放內(nèi)存;這也保證了安全(不被其他域frame js訪問)
  function destoryFrame() {
    iframe.contentWindow.document.write("");
    iframe.contentWindow.close();
    document.body.removeChild(iframe);
  }
};

// 請求跨域b頁面數(shù)據(jù)
proxy("http://www.domain2.com/b.html", function (data) {
  alert(data);
});
// 2.)proxy.html:(www.chuchur.com/proxy.html), 這個頁面可以什么都不寫,但是要保證能正常訪問
<!-- 3.)b.html:(www.chuchur.org/b.html) -->
<script>
  window.name = "我是一個可以非常長的變量";
</script>

5)postMessage跨域

postMessageHTML5 XMLHttpRequest Level 2中的API,可以解決以下方面的問題:

a.)頁面和其打開的新窗口的數(shù)據(jù)傳遞

b.)多窗口之間消息傳遞

c.)頁面與嵌套的iframe消息傳遞

d.)上面三個場景的跨域數(shù)據(jù)傳遞 用法:postMessage(data, origin)方法接受兩個參數(shù)

data:html5規(guī)范支持任意基本類型或可復(fù)制的對象,但部分瀏覽器只支持字符串,所以傳參時最好用JSON.stringify()序列化。

origin: 協(xié)議+主機+端口號,也可以設(shè)置為"\*",表示可以傳遞給任意窗口,如果要指定和當前窗口同源的話設(shè)置為"/"。

實現(xiàn):

<!-- 1.)a.html:(www.chuchur.com/a.html) -->
<iframe
  id="iframe"
  src="http://www.chuchur.com/b.html"
  style="display:none; "
></iframe>
<script>
  var iframe = document.getElementById("iframe");
  iframe.onload = function () {
    var data = {
      name: "邱秋",
    };
    // 向chuchur.org傳送跨域數(shù)據(jù)
    iframe.contentWindow.postMessage(
      JSON.stringify(data),
      "http://www.chuchur.org"
    );
  };

  // 接受chuchur.org返回數(shù)據(jù)
  window.addEventListener(
    "message",
    function (e) {
      alert("我來自chuchur.org: " + e.data);
    },
    false
  );
</script>
<!-- 2.)b.html:(www.chuchur.org/b.html) -->
<script>
  // 接收chuchur.com的數(shù)據(jù)
  window.addEventListener(
    "message",
    function (e) {
      alert("我來自chuchur.com " + e.data);
      var data = JSON.parse(e.data);
      if (data) {
        data.nick = chuchur;
        // 處理后再發(fā)回chuchur.com
        window.parent.postMessage(
          JSON.stringify(data),
          "http://www.chuchur.org"
        );
      }
    },
    false
  );
</script>

6)跨域資源共享(CORS

原理:普通跨域請求:只服務(wù)端設(shè)置Access-Control-Allow-Origin即可,前端無須設(shè)置。

cookie請求:前后端都需要設(shè)置字段,另外需注意:所帶cookie為跨域請求接口所在域的cookie,而非當前頁。 目前,所有瀏覽器都支持該功能(IE8+:IE8/9需要使用XDomainRequest對象來支持CORS),CORS也已經(jīng)成為主流的跨域解決方案。

實現(xiàn):

//1)原生js
var xhr = new XMLHttpRequest(); // IE8/9需用window. XDomainRequest兼容

// 前端設(shè)置是否帶cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.chuchur.com/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=chuchur');
xhr.onreadystatechange = function() {

  if (xhr.readyState == 4 && xhr.status == 200) {
    alert(xhr.responseText);
  }

};
//2.)jQuery ajax
$.ajax({
  ...
  xhrFields: {
    withCredentials: true // 前端設(shè)置是否帶cookie
  },
  crossDomain: true, // 會讓請求頭中包含跨域的額外信息,但不會含cookie
  ...
});
//3.)vue框架在vue-resource封裝的ajax組件中加入以下代碼:
Vue.http.options.credentials = true

//后臺服務(wù)端
//java
/*
 * 導(dǎo)入包:import javax.servlet.http. HttpServletResponse;
 * 接口參數(shù)中定義:HttpServletResponse response
 */
response.setHeader("Access-Control-Allow-Origin", "http://www.chuchur.com"); // 若有端口需寫全(協(xié)議+域名+端口)
response.setHeader("Access-Control-Allow-Credentials", "true");

//node
var server = http.createServer();
server.on('request', function(req, res) {
  var postData = '';
  // 數(shù)據(jù)塊接收中
  req.addListener('data', function(chunk) {
    postData += chunk;
  });

  // 數(shù)據(jù)接收完畢
  req.addListener('end', function() {
    postData = qs.parse(postData);
    // 跨域后臺設(shè)置
    res.writeHead(200, {
      'Access-Control-Allow-Credentials': 'true', // 后端允許發(fā)送Cookie
      'Access-Control-Allow-Origin': 'http://www.chuchur.com', // 允許訪問的域(協(xié)議+域名+端口)
      'Set-Cookie': 'l=abcdef; Path=/; Domain=www.chuchur.com; HttpOnly' // HttpOnly: 腳本無法讀取cookie
    });

    res.write(JSON.stringify(postData));
    res.end();

  });

});
server.listen('3000');

7)nginx反向代理跨域

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

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

原理:通過nginx代理一個 同域不同端口的跳板機,反向代理要跨域的域名,這樣可以修改cookie里面的domain信息實現(xiàn)跨域

實現(xiàn):

#nginx具體配置:
server {

    listen       80;
    server_name  www.chuchur.com;
    location / {
        proxy_pass   http://www.chuchur.org; #反向代理
        proxy_cookie_domain www.chuchur.org www.chuchur.com; #修改cookie里域名
        index  index.html index.htm;

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

}

前端實現(xiàn)

var xhr = new XMLHttpRequest();
// 前端開關(guān):瀏覽器是否讀寫cookie
xhr.withCredentials = true;
// 訪問nginx中的代理服務(wù)器
xhr.open('get', 'http://www.chuchur.com/?user=chuchur', true);
xhr.send();

// node
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));
  // 向前臺寫cookie
  res.writeHead(200, {

    'Set-Cookie': 'l=abcdef; Path=/; Domain=www.chuchur.org; HttpOnly' // HttpOnly: 腳本無法讀取

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

});
server.listen('8080');

8)Nodejs中間件代理跨域

原理同nignx代理跨域類似,都是通過代理服務(wù)器實現(xiàn)數(shù)據(jù)轉(zhuǎn)發(fā)

實現(xiàn):

//1)利用中間件http-proxy-middleware實現(xiàn)
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({

  // 代理跨域目標接口
  target: 'http://www.chuchur.org:',
  changeOrigin: true,
  // 修改響應(yīng)頭信息,實現(xiàn)跨域并允許帶cookie
  onProxyRes: function(proxyRes, req, res) {

    res.header('Access-Control-Allow-Origin', 'http://www.chuchur.com');
    res.header('Access-Control-Allow-Credentials', 'true');

  },

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

}));

app.listen(3000);
//2)利用中間件 webpack-dev-server實現(xiàn)
//webpack.config.js部分配置:
module.exports = {
  entry: {},
  module: {},
  ...
  devServer: {
    historyApiFallback: true,
    proxy: [{
      context: '/login',
      target: 'http://www.chuchur.org', // 代理跨域目標接口
      changeOrigin: true,
      cookieDomainRewrite: 'www.chuchur.com' // 可以為false,表示不修改
    }],
    noInfo: true

  }
}

9)WebSocket協(xié)議跨域

WebSocket protocolHTML5一種新的協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工通信,同時允許跨域通訊,是server push技術(shù)的一種很好的實現(xiàn)。原生WebSocket API使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。

實現(xiàn):

1、前端代碼

<div>user input:<input type="text" /></div>
<script src="./socket.io.js"></script>
<script>
  var socket = io("http://www.chuchur.org");

  // 連接成功處理
  socket.on("connect", function () {
    // 監(jiān)聽服務(wù)端消息
    socket.on("message", function (msg) {
      console.log("來自服務(wù)器的消息: " + msg);
    });

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

  document.getElementsByTagName("input")[0].onblur = function () {
    socket.send(this.value);
  };
</script>

2、Nodejs socket后臺:

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)聽socket連接
socket.listen(server).on('connection', function(client) {

      // 接收信息
      client.on('message', function(msg) {
          client.send('哈哈:' + msg);
          console.log('來自客服端的消息': -- - > ' + msg);
          });

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

      });

以上 9 種方式都能實現(xiàn)跨域數(shù)據(jù)傳遞,用的最多的還是第六種跨域資源共享(CORS),在前后端分離開發(fā)模式最常見。第七種和第八種中間件代理實現(xiàn)方式則是在基于node開發(fā)種常用的

其中第二,三、四、五種方案 ,利用ifamepostMessage則可以實現(xiàn) 不同窗口之間的數(shù)據(jù)通訊。

以上是“WEB前端跨域的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

web
AI