JSONP CORS 與代理解決跨域

JSONP CORS 與代理解決跨域

一.背景

​ 在我們日常的工作開發中,經常遇到跨域問題,特別是前後端分離的開發模式下,跨域問題也是非常常見的.

二.爲什麼會出現跨域?

在JavaScript中,有一個很重要的安全性限制,被稱爲“Same-Origin Policy”(同源策略)。這一策略對於JavaScript代碼能夠訪問的頁面內容做了很重要的限制,即JavaScript只能訪問與包含它的文檔在同一域下的內容。

同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。可以說 Web 是構建在同源策略基礎之上的,瀏覽器只是針對同源策略的一種實現。

​ 它的核心就在於它認爲自任何站點裝載的信賴內容是不安全的。當被瀏覽器半信半疑的腳本運行在沙箱時,它們應該只被允許訪問來自同一站點的資源,而不是那些來自其它站點可能懷有惡意的資源。

​ 瀏覽器的同源策略,是對JavaScript實施的安全限制。

​ 所謂的同源是指,域名、協議、端口均爲相同。

當前頁面url 被請求頁面url 是否跨域 原因
http://www.hello.com/ http://www.hello.com/a.html 同源(協議,域名,端口相同)
http://www.hello.com/ https://www.hello.com/a.html 跨域 協議不同(http/https)
http://www.hello.com/ http://sz.hello.com/ 跨域 子域名不同(www/sz)
http://www.hello.com:8080 http://www.hello.com:7070 跨域 端口號不同(8080/7070)
http://localhost:8080 http://127.0.0.1:8080 跨域 子域名不同(localhost/127.0.0.1)


在這裏插入圖片描述

三.解決跨域的方式

幾種主流的跨域解決方案

JSONP處理跨域
1. 原理:

​ jsonp是一種跨域通信的手段,它的原理其實很簡單:

​ a:首先是利用script標籤的src屬性來實現跨域。
b:通過將前端方法作爲參數傳遞到服務器端,然後由服務器端注入參數之後再返回,實現服務器端向客 戶端通信。
​ c: 由於使用script標籤的src屬性,因此只支持get方法

2.實現方式:
//創建script標籤
function createScript(){
    //定義一個script標籤
   let oScript = document.createElement("script");
   //script的src屬性 直接賦值爲需要跨域的接口鏈接
   //並且定義callback參數,後面跟一個回調函數
  oScript.src = `https://api.asilu.com/today?callback=getTodays`;
       //將生成的script追加到我們的dom中
       document.body.appendChild(oScript);  
  }
	//callback對於的函數  
	function getTodays(data) {
        //獲取跨域得到的對象數據
        let oTodays = data.data;
	}

​ 上述就是通過jsonp 實現了一個跨域請求,獲取天氣的數據.既然叫jsonp,顯然目的還是json,而且是跨域獲取,利用js創建一個script標籤,把json的url賦給script的scr屬性,把這個script插入到頁面裏,讓瀏覽器去跨域獲取資源,callback是頁面存在的回調方法,參數就是想得到的json,回調方法要遵從服務端的約定一般是用 callback 或者 cb,特別注意的是jsonp只針對get形式的請求

3.優缺點

​ 優點:使用簡便,沒有兼容性問題。

​ 缺點:

​ 只支持 GET 請求。

​ 前後端需要配合。

​ 由於是從其它域中加載代碼執行,因此如果其他域不安全,很可能會在響應中夾帶一些惡意代碼。

CORS跨域資源共享(xhr2)
1.原理

​ CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)- 它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制- 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與- 對於開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣- 實現CORS通信的關鍵是服務端,只要服務端實現了CORS接口,就可以跨源通信

2.實現

​ 實現CORS並不難,只需服務端做一些設置即可:如

	// 控制允許的請求來源
	header("Access-Control-Allow-Origin:*"); // *表示允許任何來源 基本上設置這個就可以了
		
	//另外,還有一些需要設置put請求 delete請求的,還有自定義請求頭,nodejs默認是get,post
    // 控制允許的自定義請求頭
    'Access-Control-Allow-Headers': 'abc,efg',
    // 控制允許的請求方式
    'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'

​ 注意:IE10以下不支持CORS

3.優缺點

​ 優點:

​ CORS 通信與同源的 AJAX 通信沒有差別,代碼完全一樣,容易維護。

​ 支持所有類型的 HTTP 請求。

缺點

​ 存在兼容性問題,特別是 IE10 以下的瀏覽器。

​ 第一次發送非簡單請求時會多一次請求。

服務器代理跨域
1.原理:

​ 通過服務端語言代理請求。如PHP,C# 服務端語言是沒有跨域限制的。- 讓服務器去別的網站獲取內容然後返回頁面。因爲同源策略是針對瀏覽器的安全性限制.服務器不存在跨域問題,所以可以由服務器請求所要域的資源再返回給客戶端。

​ 我們在請求第三方的數據時,通過瀏覽器的ajax請求肯定是不行的,因爲人家也不會給你設置cors,所以我們這邊的服務器代理解決跨域的方式就應運而生了.

2.實現

我這邊通過nodejs+express實現服務代理跨域

需要安裝中間件 http-proxy-middleware

$ npm install http-proxy-middleware

在這裏插入圖片描述

服務端代碼

// 通過代理解決跨域問題

/**
 * 使用nodejs啓動一個代理服務
 * 第三方的  http-proxy-middleware 中間件
 */

//引入express
const express = require('express');
//引入代理中間件
const { createProxyMiddleware } = require('http-proxy-middleware');
//聲明express實例
const app = express();

//  註冊一個cors的中間件,允許跨域
app.use((req, res, next) => {

    res.set({
        //允許所有域請求,實際開發中,*需要改成對應的域名
        'Access-Control-Allow-Origin': '*',
        // 設置允許前端傳遞的那些自定義請求頭
        'Access-Control-Allow-Headers': 'token',
        // 設置允許的請求方式
        'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE'
    });
	//必須要寫,將控制權交給下一個
    next();
});

// 註冊一個代理中間件
// 前端服務:http://localhost:8080
// 代理服務:http://localhost:3000
// 目標服務:http://m.maoyan.com

app.use('/api', createProxyMiddleware({
    //配置選項
    // 目標地址:只需要協議和主機
    target: "http://m.maoyan.com",
   
   /**
   * 路徑重寫
   * 
   * 當我們通過代理訪問目標接口的流程大致如下
   * http://localhost:8080 -> 請求 -> 
   * http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 -> 
   * http://m.maoyan.com/api/ajax/movieOnInfoList
   * 這時真實的目標接口地址是沒有 /api 這個前綴的。所以會導致請求失敗 404 等問題
   * 
   * 設置如下的路徑重寫規則之後,流程如下
   * http://localhost:8080 -> 請求 -> 
   * http://localhost:3000/api/ajax/movieOnInfoList -> 代理到 -> 
   * http://m.maoyan.com/ajax/movieOnInfoList
   */
    pathRewrite: {
        "^/api": ""
    },
    // 虛擬託管站點所需, 一般直接設置爲true就好
    changeOrigin: true
}));

//監聽3000端口,啓動服務
app.listen(3000, () => {
    console.log("代理服務啓動成功");
});

前端代碼

 <script>
    $(function () {
      $.ajax({
        url: 'http://localhost:3000/api/ajax/movieOnInfoList',
        type: 'GET',
        success: function (res) {
          console.log(res)
        }
      })
    })
  </script>

上面是3中主流的跨域解決方案,還有其他的解決方式,有興趣可以大家去研究:JSONP、CORS、服務器代理、document.domain、window.name、location.hash、postMessage ,nginx代理跨域

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章