跨域問題的前后端解決方案


什么是跨域?

假設有這么一個場景,我有一個網站,在里面有一個顯示商品的功能,對應的頁面地址是:

http://www.myexample.com/page/page-a.html

在實現這個頁面時,我通過iframe集成了另外一個網站的商品展示功能,對應的頁面地址是:

http://www.othersite.com/page/show.html

 
 

頁面看起來可能是這樣的,我簡化了所有的內容,通過不同的背景色來區分不同的頁面。

可以想象,我並沒有做太多的開發,就擁有一個商品展示功能了。但是在page-a頁面中,並不能通過Javascript來訪問show頁面的document、cookie等對象,不能修改show頁面中的任何內容。代碼看起來是這樣的,運行的時候會產生錯誤:

document.getElementById("iframe").contentWindow.document;

// Uncaught DOMException: Blocked a frame from accessing a cross-origin frame.

為什么這樣設計?

我們反過來思考,如果可以訪問document、cookie,會出現什么問題?

可以通過Javascript來監聽show頁面上的輸入框,可以改變表單提交的URL。在myexample這個網站就可以做任何事情了,截獲用戶的敏感數據了,比如登錄信息、個人喜好等數據。結果就是,在你訪問一個網站的時候,你的數據很容易就被泄露了,包括用戶名和密碼。

所以,簡單地說就是安全,在WWW創立之初,設計者就考慮到了這個問題,通過一些策略來保證用戶信息的安全,防止惡意的網站竊取數據。

A web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. An origin is defined as a combination of URI scheme, hostname, and port number.

這個叫同源策略,所有的瀏覽器都支持這個安全策略。從上面的定義來看,同源指的是三個相同:
1、協議(scheme)相同
2、域名(hostname)相同
3、端口(port)相同

這里又要引入一個新的概念:

URI

Uniform Resource Identifier (URI) is a string of characters used to identify a resource.

 
 

具體的語法如下圖所示:

同源概念中的scheme、host、port對應到上圖中的結構,回到我們剛才的例子:

http://www.myexample.com/page/page.html
1、scheme是http
2、host是www.myexample.com
3、port沒有表示是80

這樣就很容易理解了,跨域就是不同源,兩個不同源的網站相互會被限制,限制有三種:

1、Cookie、LocalStorage 和 IndexDB 無法讀取。
2、DOM無法獲得。
3、AJAX請求不能發送。

前端解決方案

1、修改Domain

瀏覽器允許通過設置document.domain共享 Cookie,相當於是把兩個不同源的頁面設置成相同的源,這種方法只適用於 Cookie 和 iframe 窗口,而且要求兩個網頁一級域名相同,只是二級域名不同。

document.domain='example.com';

2、window通信

這個方案簡單的說,就是通過DOM的window對象來傳遞參數。阮一峰老師總結了三點,非常清晰,我就不重復的去寫了:

片段標識符(fragment identifier)
window.name
跨文檔通信 API(Cross-document messaging)

詳情見:參考資料4

3、JSONP

在一個頁面中,可以使用<script>標記來引用一個外部的JS文件,並且能夠成功執行。直接上代碼可能會更好理解一些。

假設我在某個網站有一個js文件,URL是:http://remoteserver.com/remote.js,里面的代碼很簡單:

alert('this is a remote alert!');

在另外一個網站的頁面http://localserver.com/page.html,可以引入這個JS文件:

<head>
    <script type="text/javascript" src="http://remoteserver.com/remote.js"/>
</head>

在瀏覽器中打開page.html,會彈出一個警告對話框。

JSONP就是利用了這一點,如果將alert的內容改為下面的內容:

localMethod({"result":"data from remote!"});

同時將page.html的內容修改一下

<script type="text/javascript">
    function localMethod(data){
        alert(data.result);
    }
</script>
<script type="text/javascript" src="http://remoteserver.com/remote.js">

再次運行,alert的內容就是:data from remote!

而script元素是可以通過document.createElement('script')動態創建的,也就具備了隨時可以引入一個外部script,這樣就達到了跨域訪問的目的,但是JSONP只支持GET請求,其他的方式不支持。

4、WebSocket

WebSocket是一種通信協議,不實行同源政策,詳情見參考資料4。

上面的幾種方法,可以說是奇技淫巧,繞過了瀏覽器的限制。隨着前端框架的興起,以及前后端分離架構的流行,上面的技巧已經比較陳舊了。

后端解決方案

1、URL轉發

在同一個窗口中,通過URL提交的方式,多次跳轉,需要兩邊的頁面相互支持。

比如在http://www.myexample.com/page/page-a.html頁面中,有一個提交按鈕,將數據post到遠端的服務器,需要告訴對方跳轉回來到哪個頁面,看起來像這樣:

http://www.othersite.com/page/show.html?url=http://www.myexample.com/page/page-b.html

遠程服務器show.html處理完成后,再次將數據提交到page-b.html,page-a和page-b是在同一個域下面的,所以就可以相互訪問了。

2、HTTP代理

原理就是把遠程服務通過代理服務器變成本地的服務,需要借助WEB服務器,Nginx和Apache都支持代理轉發。Nginx的配置參考:

location /api/proxy {
    proxy_pass  http://remoteipaddress:8080/api;
    proxy_set_header  Host  $host;
    proxy_set_header  X-Real-IP  $remote_addr;
    proxy_set_header  X-Forwarded-For  $proxy_add_x_forwarded_for;
}

Node也有開源的組件http-proxy-middleware可以支持代理。

var express=require('express');
var proxy=require('http-proxy-middleware');
var app=express();
app.use('/api',proxy({target:'http://www.example.org', changeOrigin:true}));
app.listen(3000);

 

3、CORS

以上這些就是跨域的常見解決方案,其中CORS是終極解決方案,可以適用於多種場景。

 

參考資料:

1.https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy
2.https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
3.https://www.w3.org/TR/cors/
4.http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
5.http://www.ruanyifeng.com/blog/2016/04/cors.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM