什么是跨域?解決方案有哪些?,跨域解決方案有

    什么是跨域?
    跨域是指一個(gè)域下的文檔或腳本試圖去請(qǐng)求另一個(gè)域下的資源,這里跨域是廣義的。愛(ài)掏網(wǎng) - it200.com

    廣義的跨域:

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

    等dom標(biāo)簽,

    還有樣式中background:url()、
    @font-face()等文件外鏈
    3.) 腳本請(qǐng)求: js發(fā)起的ajax請(qǐng)求、dom和js對(duì)象的跨域操作等

    其實(shí)我們通常所說(shuō)的跨域是狹義的,是由瀏覽器同源策略限制的一類(lèi)請(qǐng)求場(chǎng)景。愛(ài)掏網(wǎng) - it200.com

    什么是同源策略?

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

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

    1.) Cookie、LocalStorage 和 IndexDB 無(wú)法讀取
    2.) DOM 和 Js對(duì)象無(wú)法獲得
    3.) AJAX 請(qǐng)求不能發(fā)送

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

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

    一、 通過(guò)jsonp跨域
    通常為了減輕web服務(wù)器的負(fù)載,我們把js、css,img等靜態(tài)資源分離到另一臺(tái)獨(dú)立域名的服務(wù)器上,在html頁(yè)面中再通過(guò)相應(yīng)的標(biāo)簽從不同域名下加載靜態(tài)資源,而被瀏覽器允許,基于此原理,我們可以通過(guò)動(dòng)態(tài)創(chuàng)建script,再請(qǐng)求一個(gè)帶參網(wǎng)址實(shí)現(xiàn)跨域通信。愛(ài)掏網(wǎng) - it200.com

    1)原生實(shí)現(xiàn):

             login?user=admin&callback=onBack';    

    document.head.appendChild(script);
    // 回調(diào)執(zhí)行函數(shù)
    function onBack(res) {

       alert(JSON.stringify(res));

    }

    服務(wù)端返回如下(返回時(shí)即執(zhí)行全局函數(shù)):

    onBack({"status": true, "user": "admin"})

    2)jquery ajax:

    $.ajax({
    url: 'http://www.domain2.com:8080/login',
    type: 'get', dataType: 'jsonp',
    // 請(qǐng)求方式為jsonp
    jsonpCallback: "onBack",
    // 自定義回調(diào)函數(shù)名
    data: {}
    });

    3)vue.js:

    this.$http.jsonp('
    http://www.domain2.com:8080/login',
    {
    params: {},
    jsonp: 'onBack'}).then((res) =>
    { console.log(res);
    })

    后端node.js代碼示例:

    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返回設(shè)置
    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...');

    jsonp缺點(diǎn):只能實(shí)現(xiàn)get一種請(qǐng)求。愛(ài)掏網(wǎng) - it200.com

    二、 document.domain + iframe跨域
    此方案僅限主域相同,子域不同的跨域應(yīng)用場(chǎng)景。愛(ài)掏網(wǎng) - it200.com

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

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

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

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

    • window.parent.user);

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

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

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

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

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

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

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

    • location.hash.replace('#user=', ''));
      };

    四、 window.name + iframe跨域
    window.name屬性的獨(dú)特之處:name值在不同的頁(yè)面(甚至不同域名)加載后依舊存在,并且可以支持非常長(zhǎng)的 name 值(2MB)。愛(ài)掏網(wǎng) - it200.com

    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訪(fǎng)問(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)容為空即可。愛(ài)掏網(wǎng) - it200.com

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

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

    五、 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()序列化。愛(ài)掏網(wǎng) - it200.com
    origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*",表示可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話(huà)設(shè)置為"/"。愛(ài)掏網(wǎng) - it200.com

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

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

       };        

    // 向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);

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

    data.number = 16;            

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

       }

    }, false);

    六、 跨域資源共享(CORS)
    普通跨域請(qǐng)求:只服務(wù)端設(shè)置Access-Control-Allow-Origin即可,前端無(wú)須設(shè)置,若要帶cookie請(qǐng)求:前后端都需要設(shè)置。愛(ài)掏網(wǎng) - it200.com

    需注意的是:由于同源策略的限制,所讀取的cookie為跨域請(qǐng)求接口所在域的cookie,而非當(dāng)前頁(yè)。愛(ài)掏網(wǎng) - it200.com如果想實(shí)現(xiàn)當(dāng)前頁(yè)cookie的寫(xiě)入,可參考下文:七、nginx反向代理中設(shè)置proxy_cookie_domain 和 八、NodeJs中間件代理中cookieDomainRewrite參數(shù)的設(shè)置。愛(ài)掏網(wǎng) - it200.com

    目前,所有瀏覽器都支持該功能(IE8+:IE8/9需要使用XDomainRequest對(duì)象來(lái)支持CORS)),CORS也已經(jīng)成為主流的跨域解決方案。愛(ài)掏網(wǎng) - it200.com

    1、 前端設(shè)置:
    1)原生ajax

    // 前端設(shè)置是否帶
    cookiexhr.withCredentials = true;

    示例代碼:

    var xhr = new XMLHttpRequest();
    // IE8/9需用
    window.XDomainRequest兼容
    // 前端設(shè)置是否帶
    cookiexhr.withCredentials = true;
    xhr.open('post',
    'http://www.domain2.com:8080/login', true);
    xhr.setRequestHeader
    ('Content-Type', '
    application/x-www-form-urlencoded');
    xhr.send('user=admin');
    xhr.onreadystatechange = function()
    {
    if (xhr.readyState == 4 && xhr.status == 200)
    {
    alert(xhr.responseText);
    }
    };

    2)jQuery ajax

    $.ajax({
    ...
    xhrFields: {
    withCredentials: true
    // 前端設(shè)置是否帶cookie
    },
    crossDomain: true,
    // 會(huì)讓請(qǐng)求頭中包含跨域的額外信息,
    但不會(huì)含cookie
    ...
    });

    3)vue框架
    在vue-resource封裝的ajax組件中加入以下代碼:

    Vue.http.options.credentials = true

    2、 服務(wù)端設(shè)置:
    若后端設(shè)置成功,前端瀏覽器控制臺(tái)則不會(huì)出現(xiàn)跨域報(bào)錯(cuò)信息,反之,說(shuō)明沒(méi)設(shè)成功。愛(ài)掏網(wǎng) - it200.com

    1.)Java后臺(tái):

    /*

    • 導(dǎo)入包:
      import javax.servlet.http.HttpServletResponse;
    • 接口參數(shù)中定義:
      HttpServletResponse response

    */response.setHeader
    ("Access-Control-Allow-Origin",
    "http://www.domain1.com");
    // 若有端口需寫(xiě)全(協(xié)議+域名+端口)
    response.setHeader
    ("Access-Control-Allow-Credentials", "true");

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

    var http = require('http');
    var server = http.createServer();
    var qs = require('querystring');
    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);
    // 跨域后臺(tái)設(shè)置
    res.writeHead(200, {
    'Access-Control-Allow-Credentials':
    'true',
    // 后端允許發(fā)送Cookie
    'Access-Control-Allow-Origin':
    'http://www.domain1.com',
    // 允許訪(fǎng)問(wèn)的域(協(xié)議+域名+端口)
    'Set-Cookie':
    'l=a123456;Path=/;Domain=www.domain2.com;
    HttpOnly'
    // HttpOnly:腳本無(wú)法讀取cookie

       });

    res.write(JSON.stringify(postData));
    res.end();
    });
    });
    server.listen('8080');
    console.log('Server is running at port 8080...');

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

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

    2、 nginx反向代理接口跨域

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

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

    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等中間件代理接口訪(fǎng)問(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ě)cookiexhr.withCredentials = true;
    // 訪(fǎng)問(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)證。愛(ài)掏網(wǎng) - it200.com

    1、 非vue框架的跨域(2次跨域)
    利用node + express + http-proxy-middleware搭建一個(gè)proxy服務(wù)器。愛(ài)掏網(wǎng) - it200.com

    1)前端代碼示例:

    var xhr = new XMLHttpRequest();
    // 前端開(kāi)關(guān):
    瀏覽器是否讀寫(xiě)cookiexhr.withCredentials = true;
    // 訪(fǎng)問(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框架的跨域(1次跨域)
    利用node + webpack + webpack-dev-server代理接口跨域。愛(ài)掏網(wǎng) - it200.com在開(kāi)發(fā)環(huán)境下,由于vue渲染服務(wù)和接口代理服務(wù)都是webpack-dev-server同一個(gè),所以頁(yè)面與代理接口之間不再跨域,無(wú)須設(shè)置headers跨域信息了。愛(ài)掏網(wǎng) - it200.com

    webpack.config.js部分配置:

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

       }],        

    noInfo: true
    }
    }

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

    1)前端代碼:

    user input:



    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.');
    });
    });

    原文發(fā)布時(shí)間為:2024-06-29
    本文作者:安靜de沉淀
    本文來(lái)自和通數(shù)據(jù)庫(kù)合作伙伴“Web項(xiàng)目聚集地”,了解相關(guān)信息可以關(guān)注“Web項(xiàng)目聚集地”。愛(ài)掏網(wǎng) - it200.com

    聲明:所有內(nèi)容來(lái)自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
    發(fā)表評(píng)論
    更多 網(wǎng)友評(píng)論0 條評(píng)論)
    暫無(wú)評(píng)論

    返回頂部

    主站蜘蛛池模板: 日韩欧美一区二区三区免费观看| 国产av一区二区三区日韩| 无码AⅤ精品一区二区三区| 国精产品一区二区三区糖心| 精品国产一区二区三区香蕉事 | 无码aⅴ精品一区二区三区| 国偷自产一区二区免费视频| 久久精品无码一区二区app| 国产AⅤ精品一区二区三区久久| 香蕉久久AⅤ一区二区三区 | 日韩精品无码一区二区三区免费 | 99久久精品国产高清一区二区 | 精品国产一区二区三区久| 精品无码人妻一区二区三区 | 一区二区三区无码被窝影院| 日韩免费一区二区三区在线| 日韩精品一区二区三区中文精品| 亚洲美女一区二区三区| 亚无码乱人伦一区二区| av无码免费一区二区三区| 无码乱人伦一区二区亚洲一| 亚洲大尺度无码无码专线一区| 色偷偷一区二区无码视频| 亚洲日本一区二区| 亚洲AV美女一区二区三区| 亚洲av无码一区二区三区网站| 国产精品视频一区二区三区无码 | 日韩精品一区二区三区中文精品| 人体内射精一区二区三区| 91亚洲一区二区在线观看不卡| 少妇无码一区二区二三区| 香蕉久久一区二区不卡无毒影院| 无码精品国产一区二区三区免费 | 亚洲综合一区二区三区四区五区| 韩国福利影视一区二区三区| 成人中文字幕一区二区三区| 亲子乱av一区区三区40岁| 国产福利在线观看一区二区 | 日韩经典精品无码一区| 国产午夜精品一区理论片| 一区二区三区四区视频|