同源和跨域的那些事-簡單圖文總結

image.png

01、爲什麼要跨域?

跨域的根本原因是瀏覽器的“同源策略”,得先了解什麼是同源?—— 就是【協議+域名+端口號】相同,即爲同源,只能向同源的服務發起AJAX請求。

源1 源2 是否同源
a.com b.com 🚫不同源,域名不同
http://a.com https://a.com 🚫不同源,協議不同
a.com:80 a.com:443 🚫不同源,端口不同
gg.com a.gg.com 🚫不同源,子域名不同
a.com/ss a.com/s2 同源

可通過location.originwindow.origin獲取當前文檔的源

❓爲什麼要同源呢?

這是瀏覽器故意設計的,是瀏覽器的基本安全策略,否則會很容易受到XSS、CSRF攻擊。只能向同源的服務發起AJAX請求,不可跨域請求,會被瀏覽器攔截。

❓有哪些限制規則呢?

  • ✅ 訪問其他源的圖片、CSS、JS是可以的,允許<img src="url"><link href="url"><script src="url">元素獲取的其他源的資源。
  • ✅ Form表單可以跨域提交,表單的提交只是提交數據無需返回,瀏覽器認爲是安全的。
  • 🚫 AJAX不可以向其他源發送網絡請求,會被瀏覽器攔截。注意攔截的不是請求,而是響應,服務端依然是可以收到請求的。
  • 🚫 僅可訪問自己域的cookie、localStorage、DOM樹,不能訪問Iframe嵌入的其他頁面內部內容。

image


02、如何實現跨域?

隨着互聯網越來越複雜,需求也越來越多,跨域請求就很常見了。我們知道了跨域是瀏覽器的同源限制,就可以針對性的想辦法了。

2.1、JSONP跨域

這是一種傳統的跨域請求辦法,藉助於<script>標籤元素,因爲<script>src可以訪問任何站點的資源。當然這需要服務端對應接口支持JSONP(JSON with padding)協議,所以是需要雙方約定好,所以瀏覽器認爲這是安全的。

  • 優點是兼任IE,實現跨域。
  • 缺點是不能控制請求過程,僅支持GET方式請求。因爲只是一個<script>標籤,瀏覽器自動發起的資源請求。

JSONP(JSON with Padding)是JSON的一種”使用模式“,是一種非官方的協議,用於解決瀏覽器的跨域數據訪問的問題。

📢前端具體實現過程:

  • 1、申明一個全局的回調函數“getData”來接收數據。
  • 2、動態創建一個<script>標籤,src爲要跨域的API地址,URL中帶上回調參數“callback=getData”。
  • 3、服務端收到請求後,動態生成一個腳本,腳本內容是一個字符串,由回調+返回的數據構成:“getData('data')”。
  • 4、本地執行遠程腳本,回調函數“getData”運行,就得到了想要的數據。
<script src="http:www.thrid.com/cors/api?q=key&callback=back"></script>
<script>
  function back(data) {
    console.log(data);
  }
</script>

JSONP的實現:

function jsonp(url, args, cbName) {
  return new Promise((resolve, reject) => {
    const ele = document.createElement('script');
    window[cbName] = (data) => {
      resolve(data);
      document.body.removeChild(ele);
    }
    args = { ...args, callback: cbName };
    ele.src = `${url}?${Object.keys(args).map(k => `${k}=${args[k]}`).join('&')}`;
    document.body.appendChild(ele);
  });
}
//使用,api爲360的公開接口
jsonp('https://sug.so.360.cn/suggest', { format: 'jsonp', word: 'china' }, 'search')
  .then(function (data) {
    console.log(data)
  });

2.2、CORS跨域

CORS是什麼?—— 跨域資源共享 (cross-origin resource sharing),讓AJAX可以跨域訪問數據。這是爲了滿足跨域請求的需求,W3C新增加的特性,需要服務端的支持,不支持IE8/9。根據請求方式,瀏覽器將CORS分爲兩種情況:

  • 簡單請求(安全請求):只支持GET、POST、HEAD,Header只支持部分字段。
  • 複雜請求(其他請求):簡單請求以外的其他跨域請求。

🔵簡單請求

基本原理就是在請求頭加入一個身份來源標識,服務端根據這個標識來判等是否允許訪問,如果允許則給一個允許的標記並返回響應。

  • 只支持GET、POST、HEAD。
  • header —— 我們僅能設置基礎的安全字段:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type 的值爲 application/x-www-form-urlencoded,multipart/form-data 或 text/plain。

📢具體過程比較簡單,前端只要在Header加入“Origin”即可:

  • 請求頭Header加入要跨域的源:origin:http://www.main.com
GET /api HTTP/1.1
Origin: http://www.main.com				//本次請求來自哪個源
Host: http://www.third.com				//請求的第三方API
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0
...
  • 服務端收到請求後檢查Origin,如果同意請求則正常響應,同時在響應的Header中加入特殊的“Access-Control-Allow-Origin”字段,申明支持的源,也可以用“*”表示支持任何源訪問。
  • 瀏覽器收到響應後會檢查“Access-Control-Allow-Origin”,和當前源對比,如果不合法則會報錯——跨域。
Access-Control-Allow-Origin: http://www.main.com		//請求允許的源
Access-Control-Allow-Credentials: true							//是否允許cookie,cors默認不發送cookie,如果要發送,還需AJAX中設置withCredentials
Access-Control-Expose-Headers: Content-Length,API-Key	//如果客戶端想要訪問其他非安全字段,則需要服務端明確定義哪些Header字段暴露出來
Content-Type: text/html; charset=utf-8

image.png

🟠複雜請求

不是簡單請求的都稱爲複雜請求(非簡單請求),如請求方法是PUT、DELETE,或Content-Type=application/json。相比於簡單請求,複雜請求多了一次預請求。

預請求

  • 正式發送請求前,瀏覽器會自動發送一個預請求,問問服務端是否允許本次請求,如果迴應允許才正式發送請求,後面就和簡單請求相同了。
  • 預請求及其響應都沒有body,採用OPTIONS方法。

image.png


03、跨域小結

因爲同源是瀏覽器的限制,跨域的方法無非就是繞過,或採用CORS。

跨域方案 基本原理 是否需要服務端支持
JSONP 藉助<script>標籤的src,加上一個全局回調函數接收數據 🟠需要服務端支持JSONP協議
CORS W3C標準支持的跨域方式,請求頭添加Origin字段 🟠需要服務端支持
WebSocket WebSocket可以實現瀏覽器與服務端的雙向通信,沒有跨域的困惑。推薦第三方庫 Socket.io,可以很方便的建立與服務端的Socket通信。 🟠需要服務端支持,支持WebSocket
iframe+postMessage 使用window.postMessage()來實現窗口之間的通信 🔵不需服務端處理,客戶端繞過
服務端代理 由自己的同源服務端代理第三方的請求 🟠需要服務端支持,代理請求
nginx反向代理 原理和服務端代理一樣,用nginx配置一個代理服務 🔵不需要服務端修改代碼,需nginx支持

參考資料


©️版權申明:版權所有@安木夕,本文內容僅供學習,歡迎指正、交流,轉載請註明出處!原文編輯地址-語雀

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