溫馨提示×

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

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

為什么要研究跨域問(wèn)題

發(fā)布時(shí)間:2020-12-05 11:12:50 來(lái)源:億速云 閱讀:186 作者:小新 欄目:web開(kāi)發(fā)

這篇文章將為大家詳細(xì)講解有關(guān)為什么要研究跨域問(wèn)題,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

跨域,老生常談的問(wèn)題

簡(jiǎn)述

作為一只前端菜鳥(niǎo),跨域方面只懂得JSONP和CORS,并未曾深入了解。但隨著春招越來(lái)越近,就算是菜鳥(niǎo)也要猛振翅膀。近幾日仔細(xì)研究了跨域問(wèn)題,寫下這篇文章,希望對(duì)開(kāi)發(fā)者們有所幫助。在讀本文前,希望您對(duì)以下知識(shí)略有了解。

瀏覽器同源策略

nodejs

iframe

docker, nginx

我們?yōu)楹我芯靠缬騿?wèn)題

因?yàn)闉g覽器的同源策略規(guī)定某域下的客戶端在沒(méi)明確授權(quán)的情況下,不能讀寫另一個(gè)域的資源。而在實(shí)際開(kāi)發(fā)中,前后端常常是相互分離的,并且前后端的項(xiàng)目部署也常常不在一個(gè)服務(wù)器內(nèi)或者在一個(gè)服務(wù)器的不同端口下。前端想要獲取后端的數(shù)據(jù),就必須發(fā)起請(qǐng)求,如果不錯(cuò)一些處理,就會(huì)受到瀏覽器同源策略的約束。后端可以收到請(qǐng)求并返回?cái)?shù)據(jù),但是前端無(wú)法收到數(shù)據(jù)。

多種跨域方法

跨域可以大概分為兩種目的

前后端分離時(shí),前端為了獲取后端數(shù)據(jù)而跨域

為不同域下的前端頁(yè)面通信而跨域

為前后端分離而跨域

Cross Origin Resource Share (CORS)

CORS是一個(gè)跨域資源共享方案,為了解決跨域問(wèn)題,通過(guò)增加一系列請(qǐng)求頭和響應(yīng)頭,規(guī)范安全地進(jìn)行跨站數(shù)據(jù)傳輸

請(qǐng)求頭主要包括

請(qǐng)求頭解釋
OriginOrigin頭在跨域請(qǐng)求或預(yù)先請(qǐng)求中,標(biāo)明發(fā)起跨域請(qǐng)求的源域名。
Access-Control-Request-MethodAccess-Control-Request-Method頭用于表明跨域請(qǐng)求使用的實(shí)際HTTP方法
Access-Control-Request-HeadersAccess-Control-Request-Headers用于在預(yù)先請(qǐng)求時(shí),告知服務(wù)器要發(fā)起的跨域請(qǐng)求中會(huì)攜帶的請(qǐng)求頭信息

響應(yīng)頭主要包括

響應(yīng)頭解釋
Access-Control-Allow-OriginAccess-Control-Allow-Origin頭中攜帶了服務(wù)器端驗(yàn)證后的允許的跨域請(qǐng)求域名,可以是一個(gè)具體的域名或是一個(gè)*(表示任意域名)。
Access-Control-Expose-HeadersAccess-Control-Expose-Headers頭用于允許返回給跨域請(qǐng)求的響應(yīng)頭列表,在列表中的響應(yīng)頭的內(nèi)容,才可以被瀏覽器訪問(wèn)。
Access-Control-Max-AgeAccess-Control-Max-Age用于告知瀏覽器可以將預(yù)先檢查請(qǐng)求返回結(jié)果緩存的時(shí)間,在緩存有效期內(nèi),瀏覽器會(huì)使用緩存的預(yù)先檢查結(jié)果判斷是否發(fā)送跨域請(qǐng)求。
Access-Control-Allow-MethodsAccess-Control-Allow-Methods用于告知瀏覽器可以在實(shí)際發(fā)送跨域請(qǐng)求時(shí),可以支持的請(qǐng)求方法,可以是一個(gè)具體的方法列表或是一個(gè)*(表示任意方法)。
如何使用
  • 客戶端只需按規(guī)范設(shè)置請(qǐng)求頭。

  • 服務(wù)端按規(guī)范識(shí)別并返回對(duì)應(yīng)響應(yīng)頭,或者安裝相應(yīng)插件,修改相應(yīng)框架配置文件等。具體視服務(wù)端所用的語(yǔ)言和框架而定

SpringBoot 設(shè)置CORS例子

一個(gè)spring boot項(xiàng)目中關(guān)于CORS配置的一段代碼

HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        String temp = request.getHeader("Origin");
        httpServletResponse.setHeader("Access-Control-Allow-Origin", temp);
        // 允許的訪問(wèn)方法
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH");
//         Access-Control-Max-Age 用于 CORS 相關(guān)配置的緩存
        httpServletResponse.setHeader("Access-Control-Max-Age", "3600");
        httpServletResponse.setHeader("Access-Control-Allow-Headers",
                "Origin, X-Requested-With, Content-Type, Accept,token");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");

JSONP 跨域

jsonp的原理就是借助HTML中的<script>標(biāo)簽可以跨域引入資源。所以動(dòng)態(tài)創(chuàng)建一個(gè)<srcipt>標(biāo)簽,src為目的接口 + get數(shù)據(jù)包 + 處理數(shù)據(jù)的函數(shù)名。后臺(tái)收到GET請(qǐng)求后解析并返回函數(shù)名(數(shù)據(jù))給前端,前端<script>標(biāo)簽動(dòng)態(tài)執(zhí)行處理函數(shù)
觀察下面代碼

前端代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var script = document.createElement('script');
    script.type = 'text/javascript';

    // 傳參并指定回調(diào)執(zhí)行函數(shù)為getData
    script.src = 'http://localhost:8080/users?username=xbc&callback=handleData';
    document.body.appendChild(script);
    // 回調(diào)執(zhí)行函數(shù)
    function handleData(res) {
        data = JSON.stringify(res)
        console.log(data);
    }
</script>
</body>
</html>

后端代碼(nodejs)

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

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

    // jsonp返回設(shè)置
    res.writeHead(200, { 'Content-Type': 'text/javascript' });
    var data = {
        user: 'xbc',
        password: '123456'
    }
    res.write(fn + '(' + JSON.stringify(data) + ')');

    res.end();
});

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

在該例子中,前臺(tái)收到的res是這樣的

為什么要研究跨域問(wèn)題

前端頁(yè)面是這樣的

為什么要研究跨域問(wèn)題

注意

JSONP既是利用了<srcipt>,那么就只能支持GET請(qǐng)求。其他請(qǐng)求無(wú)法實(shí)現(xiàn)

nginx 反向代理實(shí)現(xiàn)跨域

思路

既然瀏覽器有同源策略限制,那我們把前端項(xiàng)目和前端要請(qǐng)求的api接口地址放在同源下不就可以了?再結(jié)合web服務(wù)器提供的反向代理,便可以在前端和后端都不做配置的情況下解決跨域問(wèn)題。

以nginx為例

后端真實(shí)后臺(tái)地址:http://xxx.xxx.xxx.xxx:8085 后臺(tái)地址使用tomcat部署的spring boot項(xiàng)目 名為gsms_test

nginx服務(wù)器地址: http://xxx.xxx.xxx.xxx:8082

tomcat和nginx都是用docker架設(shè)的,做了端口轉(zhuǎn)發(fā)

使用條件:開(kāi)發(fā)環(huán)境為linux系統(tǒng)

nginx /etc/nginx/conf.d/default.conf配置代碼如下

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        # root   /usr/share/nginx/html/dist; # 前端項(xiàng)目路徑
        # index  index.html index.htm;
        proxy_pass http://localhost:8001/; # 前端本機(jī)地址,實(shí)現(xiàn)自動(dòng)更新
        autoindex on;
        autoindex_exact_size on;
        autoindex_localtime on;
    }

    location /gsms_test/ {
        proxy_pass 后端真實(shí)地址;
    }

    

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    # proxy the PHP scripts to Apache listening on 127.0.0.1:80
    #
    #location ~ \.php$ {
    #    proxy_pass   http://127.0.0.1;
    #}

    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
    #
    #location ~ \.php$ {
    #    root           html;
    #    fastcgi_pass   127.0.0.1:9000;
    #    fastcgi_index  index.php;
    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    #    include        fastcgi_params;
    #}

    # deny access to .htaccess files, if Apache's document root
    # concurs with nginx's one
    #
    #location ~ /\.ht {
    #    deny  all;
    #}
}

不同域下頁(yè)面通信而跨域

window.name + iframe 跨域

window.name是瀏覽器中一個(gè)窗口所共享的數(shù)據(jù),在不同的頁(yè)面(甚至不同域名)加載后依舊存在(如果沒(méi)修改則值不會(huì)變化),并且可以支持非常長(zhǎng)的 name 值(2MB)。比如 a域的某頁(yè)面想獲取b域某頁(yè)面的數(shù)據(jù),可以在b域中修改window.name值,a域切換到b域再切回來(lái)即可得到b域的window.name值。可是我們?cè)陂_(kāi)發(fā)中肯定不想頁(yè)面切來(lái)切去,所以就要結(jié)合iframe來(lái)實(shí)現(xiàn)。

示例 (以thinkjs實(shí)現(xiàn))

a 域代碼如下

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>A 域</title>
</head>
<body>
<h2>server A</h2>
<script type="text/javascript">
    function getData() {
        var iframe = document.getElementById('proxy');
        iframe.onload = function () {
            var name = iframe.contentWindow.name; // 獲取iframe窗口里的window.name值
            console.log(name)
        }
        // 由于iframe信息傳遞也受同源策略限制,所以在window.name被B域修改后,將iframe轉(zhuǎn)回A域下。以便獲取iframe的window.name值
        iframe.src = 'http://127.0.0.1:8360/sub.html' 
    }
</script>
<iframe id="proxy" src="http://127.0.0.1:8361/index.html" style="width: 100%" onload="getData()">        </iframe>
</body>
</html>

b 域代碼

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>New ThinkJS Application</title>
</head>
<body>
  <h2>server 2</h2>
<script type="text/javascript">
  window.name = 'user: xbc';
</script>
</body>
</html>
注意

由于受同源策略限制,父頁(yè)面獲取跨域的iframe頁(yè)面的信息不全,所以要在iframe的window.name被B域修改后,轉(zhuǎn)為A域下的任一頁(yè)面(該一面不得修改window.name),在進(jìn)行獲取。

代理頁(yè)面 + iframe 實(shí)現(xiàn)跨域訪問(wèn)

由于iframe與父頁(yè)面相互訪問(wèn)也受同源策略限制,所以要借助一代理頁(yè)面實(shí)現(xiàn)跨域。

為什么要研究跨域問(wèn)題

個(gè)人認(rèn)為有些麻煩,若有興趣請(qǐng)看前端如何用代理頁(yè)面解決iframe跨域訪問(wèn)的問(wèn)題?

總結(jié)

以上幾種皆是本人用過(guò)或測(cè)試過(guò)的跨域方法,還有postMessage,WebSocket等跨域方法由于從未接觸不做說(shuō)明。在項(xiàng)目中具體使用那些方法還需具體考慮各種問(wèn)題

情況方法
只有GET請(qǐng)求JSONP
對(duì)兼容性及瀏覽器版本無(wú)要求CORS
對(duì)兼容性及瀏覽器版本有要求iframe 或 服務(wù)器反向代理(linux 環(huán)境下開(kāi)發(fā))

關(guān)于為什么要研究跨域問(wèn)題就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

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

AI