1. 程式人生 > 實用技巧 >前端常見瀏覽器跨域請求解決方案

前端常見瀏覽器跨域請求解決方案

在瀏覽器請求中,出現跨域訪問資源的問題,我們肯定會遇到。如果跨域請求被阻止,有可能導致css、js 、ajax請求、font字型等資源出現無法正常訪問的問題。

跨域

跨域是指一個域下的文件或者指令碼試圖去請求另一個域下的資源。

廣義的跨域
  1. 資源跳轉:a連結跳轉、重定向、表單提交;
  2. 資源嵌入:<link><script><frame>等DOM操作標籤;
  3. js 發起的 ajax 請求、dom 和 js 物件的跨域操作等;
狹義的跨域

瀏覽器同源策略限制

同源策略

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

同源策略限制:
  1. CookieLocalStorageIndexDB 無法讀取
  2. DOMJS 物件無法獲取
  3. AJAX 請求不能傳送
注意:
  1. 如果是協議和埠造成的跨域問題“前臺”是無能為力的;
  2. 在跨域問題上,域僅僅是通過“URL的首部”來識別而不會去嘗試判斷相同的ip地址對應著兩個域或兩個域是否在同一個ip上。

前端跨域解決方案

一、jsonp跨域

  • 通常為了減輕web伺服器的負載,我們把js、css,img等靜態資源分離到另一臺獨立域名的伺服器上,在html頁面中再通過相應的標籤從不同域名下載入靜態資源,而被瀏覽器允許,基於此原理,我們可以通過動態建立script,再請求一個帶參網址實現跨域通訊。
  • 簡單點來說就是:動態建立script,再請求一個帶參網址實現跨域通訊(script 標籤的 src 屬性不受同源策略的限制 );
  • 缺點是隻能實現 get 一種請求,容易遭受 xss 攻擊。

二、CORS(跨域資源共享)

普通跨域請求:只服務端設定Access-Control-Allow-Origin即可,前端無須設定,若要帶cookie請求:前後端都需要設定。
需注意的是:由於同源策略的限制,所讀取的cookie為跨域請求介面所在域的cookie,而非當前頁。
目前,所有瀏覽器都支援該功能(IE8+:IE8/9需要使用XDomainRequest物件來支援CORS)),CORS也已經成為主流的跨域解決方案。

# 配置 cors 跨域
header("Access-Control-Allow-Origin:*");
header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS");
header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');

三、nginx配置代理(proxy)

配置代理分為正向代理和反向代理兩種形式。

正向代理
  • 有一個客戶端需要向一個非同源的伺服器B傳送請求,我們搭建一個和客戶端同源的伺服器A,當客戶端傳送請求的時候,由伺服器A來接受,再由伺服器A向伺服器B傳送請求,因為 同源策略是由瀏覽器給的,伺服器之間沒有。
  • 伺服器B接受到請求以後,會處理請求,並把響應返回給伺服器A,再由伺服器A把響應給到客戶端就可以了。我們就可以用這個方式來進行跨域請求了。
反向代理
  • 反向代理一般是用來做負載均衡
  • 當我請求一個伺服器的時候,其實請求的是伺服器端設定的代理伺服器,由代理伺服器把若干大量的請求分發給不同的伺服器進行處理,再由伺服器把響應給到代理伺服器,代理伺服器返回給客戶端。
伺服器配置檔案
  • ngnix 後臺配置
server {
	   # 監聽的埠號
	   listen        80;
	   # 伺服器名稱;
	   server_name  localhost;
	   # 根目錄;
	   root   "D:/phpstudy_pro/WWW";
	
	   # 配置的代理;(在這裡配置)
	   location = /baidu {
	       proxy_pass https://www.baidu.com/sugrec;
	   }
	   location = /pxx {
	       proxy_pass https://apiv2.pinduoduo.com/api/gindex/subject/limited/goods;
	   }
	
	   # location => 位址列資訊;
	   # http://localhost/php/day26/xxx.html;
	   # http://   : 給瀏覽器看的 , 讓瀏覽器知道用什麼樣的方式去進行請求的傳送,響應的接受;
	   # localhost : 給瀏覽器看的,讓瀏覽器可以向對應的ip地址(目標)發起請求;
	   # /php/day26/xxx.html 伺服器查詢語句;
	   # 在nginx之中由 location 來進行捕獲並處理;
	   # http://localhost : 預設就是傳遞一個 / 給伺服器;
	   # file:///D:/phpstudy_pro/WWW/index.html
	
	   # 重要 : 如果更改了配置檔案,那麼必須重啟伺服器;
	   location / {
	       index index.php index.html;
	       error_page 400 /error/400.html;
	       error_page 403 /error/403.html;
	       error_page 404 /error/404.html;
	       error_page 500 /error/500.html;
	       error_page 501 /error/501.html;
	       error_page 502 /error/502.html;
	       error_page 503 /error/503.html;
	       error_page 504 /error/504.html;
	       error_page 505 /error/505.html;
	       error_page 506 /error/506.html;
	       error_page 507 /error/507.html;
	       error_page 509 /error/509.html;
	       error_page 510 /error/510.html;
	       autoindex  on;
	   }
	
	   # .php字尾的內容需要用php解析;
	   # /\.php(.*)$/ 正則
	   location ~ \.php(.*)$ {
	       fastcgi_pass   127.0.0.1:9000;
	       fastcgi_index  index.php;
	       fastcgi_split_path_info  ^((?U).+\.php)(/?.+)$;
	       fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
	       fastcgi_param  PATH_INFO  $fastcgi_path_info;
	       fastcgi_param  PATH_TRANSLATED  $document_root$fastcgi_path_info;
	       include        fastcgi_params;
	   }   
}
  • 前端 js 配置
<script>
   // 1. 找到nginx伺服器配置檔案;
    // 2. 進行代理配置;
    var options = {
        url : "http://localhost/baidu",
        success : function(res){
            console.log(res);
        },
        dataType : "json",
        data : {
            wd : "你好",
            prod : "pc",
            sugsid : "1423,31169,21125,30839,31187,30823,22159",
        }
    }
    ajax(options)
</script>

四、Nodejs中介軟體代理

node中介軟體實現跨域代理,原理大致與nginx相同,都是通過啟一個代理伺服器,實現資料的轉發
使用 node + express + http-proxy-middleware 搭建一個proxy伺服器。

  • 前端程式碼示例
	var xhr = new XMLHttpRequest();
	
	// 前端開關,瀏覽器是否讀寫cookie
	xhr.withCredentials = true;
	
	// 訪問http-proxy-middLeware代理服務爨
	xhr.open( 'get', 'http://www.domain1.com:3000/login?user=admin', true);
	xhr.send();
  • 中介軟體伺服器程式碼示例
	var express = require( 'express' );
	var proxy = require( 'http-proxy-middleware');
	var app = express();
	
	app.use("/" , proxy({
		// 代理跨域目標介面
		target: 'http://www.domain2.com:808e',
		changeorigin: true,
		// 修改響應頭資訊,實現跨域並允許帶cookie
		onProxyRes: function(proxyRes, req, res) {
			res.header( 'Access-Control-Allow-Origin', 'http://www.domain1.com');
			res.header( 'Access-Control-Allow-Credentials ', 'true' );
		},
		// 修改響應資訊中的cookie域名
		cookieDomainRewrite: 'www.domain1.com' //可以為false,表示不修改
	}));
	app.listen( 3000);
	console.log('Proxy server is listen at port 3000...');
  • nodejs 後臺程式碼示例同 三、nginx配置代理(proxy)

五、WebSocket協議

web sockets是一種瀏覽器的API,它的目標是在一個單獨的持久連線上提供全雙工、雙向通訊。(同源策略對web sockets不適用)

web sockets原理:在JS建立了web socket之後,會有一個HTTP請求傳送到瀏覽器以發起連線。取得伺服器響應後,建立的連線會使用HTTP升級從HTTP協議交換為web sockt協議。

只有在支援web socket協議的伺服器上才能正常工作。

var socket = new WebSockt('ws://www.baidu.com'); //http->ws; https->wss
socket.send('hello WebSockt');
socket.onmessage = function(event){
    var data = event.data;
}