jsonp-反向代理-CORS解決JS跨域問題的個人總結

jsonp-反向代理-CORS解決JS跨域問題的個人總結

網上說了很多很多,但是看完之后還是很混亂,所以我自己重新總結一下。

解決 js 跨域問題一共有8種方法,

  1. jsonp(只支持 get)
  2. 反向代理
  3. CORS
  4. document.domain + iframe 跨域
  5. window.name + iframe 跨域
  6. window.postMessage
  7. location.hash + iframe
  8. web sockets

各個方法都有各自的優缺點,但是目前前端開發方面比較常用的是 jsonp,反向代理,CORS:

  • CORS是跨源資源分享(Cross-Origin Resource Sharing)的縮寫。它是W3C標準,是跨源AJAX請求的根本解決方法。優點是正統,符合標準,缺點是:

    • 但是需要服務器端配合,比較麻煩。
  • JSONP 優點是對舊式瀏覽器支持較好,缺點是:

    • 但是只支持 get 請求。
    • 有安全問題(請求代碼中可能存在安全隱患)。
    • 要確定jsonp請求是否失敗并不容易
  • 反向代理都能夠兼容以上的確定,但是僅僅作為前端開發模式的時候使用,在正式上線環境較少用到。

    • 因為開發環境的域名跟線上環境不一樣才需要這樣處理。
    • 如果線上環境太復雜,本身也是多域(后面說到的同源策略問題,多子域,或者多端口問題),那么需要采用 jsonp 或者 CORS 來處理。
這里主要說明這三種方式。其他方式暫不說明。

一、什么是跨域問題

跨域問題一般只出現在前端開發中使用 javascript 進行網絡請求的時候,瀏覽器為了安全訪問網絡請求的數據而進行的限制。

提示的錯誤大致如下:

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://XXXXXX' is therefore not allowed access.

clipboard.png

二、為什么會出現跨域問題

因為瀏覽器收到同源策略的限制,當前域名的js只能讀取同域下的窗口屬性。

2.1 同源策略

同源指的是三個源頭同時相同:

  • 協議相同
  • 域名相同
  • 端口相同

舉例來說,http://www.example.com/dir/page.html這個網址,

協議是 http://
域名是 www.example.com
端口是80 //它的同源情況如下:
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)

同源策略限制了以下行為:

  • Cookie、LocalStorage 和 IndexDB 無法讀取
  • DOM 和 JS 對象無法獲取
  • Ajax請求發送不出去
大概可以知道跨域其實就是同源策略導致的,并且知道同源策略的原理。

詳細的同源策略相關,可以參考http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html

三、解決跨域問題

3.1 反向代理方式

反向代理和正向代理的區別:

  • 正向代理(Forward Proxy),通常都被簡稱為代理,就是在用戶無法正常訪問外部資源,比方說受到GFW的影響無法訪問twitter的時候,我們可以通過代理的方式,讓用戶繞過防火墻,從而連接到目標網絡或者服務。
  • 反向代理(Reverse Proxy)是指以代理服務器來接受 Internet 上的連接請求,然后將請求轉發給內部網絡上的服務器,并將從服務器上得到的結果返回給 Internet 請求連接的客戶端,此時,代理服務器對外就表現為一個服務器。

那么我們可以理解為反向代理

如何使用反向代理服務器來達到跨域問題解決:

  • 前端ajax請求的是本地反向代理服務器
  • 本地反向代理服務器接收到后:

    • 修改請求的 http-header 信息,例如 referer,host,端口等
    • 修改后將請求發送到實際的服務器
  • 實際的服務器會以為是同源(參考同源策略)的請求而作出處理

現在前端開發一般使用 nodejs來做本地反向代理服務器

// 在 express 之后引入路由
var app = express();var apiRoutes = express.Router();app.use(bodyParser.urlencoded({extended:false}))// 自定義 api 路由
apiRoutes.get("/lyric", function (req, res) {var url = "https://c.y.qq.com/lyric/fcgi-bin/fcg_query_lyric_new.fcg";axios.get(url, {headers: { // 修改 headerreferer: "https://c.y.qq.com/",host: "c.y.qq.com"},params: req.query}).then((response) => {var ret = response.dataif (typeof ret === "string") {var reg = /^\w+\(({[^()]+})\)$/;var matches = ret.match(reg);if (matches) {ret = JSON.parse(matches[1])}}res.json(ret)}).catch((e) => {console.log(e)})
});// 使用這個路由
app.use("/api", apiRoutes);

3.2 JSONP 方式

JSONP有些文章會叫動態創建script,因為他確實是動態寫入 script 標簽的內容從而達到跨域的效果:

  • AJAX 無法跨域是受到“同源政策”的限制,但是帶有src屬性的標簽(例如<script>、<img>、<iframe>)是不受該政策限制的,因此我們可以通過向頁面中動態添加<script>標簽來完成對跨域資源的訪問,這也是 JSONP 方案最核心的原理,換句話理解,就是利用了【前端請求靜態資源的時候不存在跨域問題】這個思路。
  • JSONP(JSON with Padding)是數據格式JSON的一種“使用模式”。
  • JSONP 只能用 get 方式。

實現 jsonp 的方式:

clipboard.png

引用來自https://segmentfault.com/a/1190000012469713的圖

  • 客戶端和服務器端約定一個參數名是代表 jsonp 請求的,例如約定 callback 這個參數名。
  • 然后服務器端準備好針對之前約定的 callback 參數請求的 javascript 文件,這個文件里面要有一個函數名,要跟客戶端請求的時候的函數名要保持一致。(如下面例子:ip.js
  • 然后客戶端注冊一個本地運行的函數,并且函數的名字要跟去請求服務器進行 callback 回調的函數的名字要一致。(如下面例子:foo 函數跟請求時候callback=foo的名字是一致的)
  • 然后客戶端對服務器端進行 jsonp 的方式請求。
  • 服務器端返回剛才配置好的js 文件(ip.js)到客戶端
  • 客戶端瀏覽器,解析script標簽,并執行返回的javascript文件,此時數據作為參數,傳入到了客戶端預先定義好的 callback 函數里。

    • 相當于本地執行注冊好foo 函數,然后獲取了一個foo 函數,并且這個獲取的 foo 函數里面包含了傳入的參數(例如 foo({XXXXX})

這是一個實例 demo:

服務器端文件ip.js

foo({"ip": "8.8.8.8"
});

客戶端文件 jsonp.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title><script>// 動態插入 script 標簽到 html 中function addScriptTag(src) {var script = document.createElement('script');script.setAttribute("type","text/javascript");script.src = src;document.body.appendChild(script);}// 獲取 jsonp 文件window.onload = function () {addScriptTag('http://example.com/ip?callback=foo');}// 執行本地的 js 邏輯,這個要跟獲取到的 jsonp 文件的函數要一致function foo(data) {console.log('Your public IP address is: ' + data.ip);};</script>
</head>
<body>
</body>
</html>

3.3 CORS 方式

CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源服務器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。

  • CORS需要瀏覽器和服務器同時支持。目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。
  • 整個CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發現AJAX請求跨源,就會自動添加一些附加的頭信息,有時還會多出一次附加的請求,但用戶不會有感覺。
因此,實現CORS通信的關鍵是服務器端。只要服務器端實現了CORS接口,就可以跨源通信。

3.3.1 CORS的請求分為兩類:

  • 簡單請求
  • 非簡單請求

只要同時滿足以下兩大條件,就屬于簡單請求。

(1) 請求方法是以下三種方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的頭信息不超出以下幾種字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同時滿足上面兩個條件,就屬于非簡單請求。

3.3.2 簡單請求

如果是簡單請求的話,會自動在頭信息之中,添加一個Origin字段

GET /cors HTTP/1.1
Origin: http://api.bob.com 
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

這個Origin對應服務器端的Access-Control-Allow-Origin設置,所以一般來說需要在服務器端加上這個Access-Control-Allow-Origin 指定域名|*

3.3.3 非簡單請求

如果是非簡單請求的話,會在正式通信之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。

瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可以使用哪些HTTP動詞和頭信息字段。只有得到肯定答復,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。

需要注意這里是會發送2次請求,第一次是預檢請求,第二次才是真正的請求!

首先發出預檢請求:

// 預檢請求
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..

除了Origin字段,"預檢"請求的頭信息包括兩個特殊字段。

(1)Access-Control-Request-Method

該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。

(2)Access-Control-Request-Headers

該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header。

然后服務器收到"預檢"請求以后:

檢查了OriginAccess-Control-Request-MethodAccess-Control-Request-Headers字段以后,確認允許跨源請求,就可以做出回應。

// 預檢請求的回應
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

最后一旦服務器通過了"預檢"請求:

以后每次瀏覽器正常的CORS請求,就都跟簡單請求一樣,會有一個Origin頭信息字段。服務器的回應,也都會有一個Access-Control-Allow-Origin頭信息字段。

// 以后的請求,就像拿到了通行證之后,就不需要再做預檢請求了。
PUT /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

詳情參考這里http://www.ruanyifeng.com/blog/2016/04/cors.html


參考文檔:

  • 前端解決跨域問題的8種方案
  • 瀏覽器同源政策及其規避方法
  • https://tonghuashuo.github.io/blog/jsonp.html
  • http://www.cnblogs.com/yuzhongwusan/archive/2012/12/11/2812849.html
  • http://www.cnblogs.com/dowinning/archive/2012/04/19/json-jsonp-jquery.html
  • https://segmentfault.com/a/1190000002438126

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/284364.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/284364.shtml
英文地址,請注明出處:http://en.pswp.cn/news/284364.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

URAL 1682 Crazy Professor (并查集)

【題目鏈接】 http://acm.timus.ru/problem.aspx?space1&num1682 【題目大意】 給出k&#xff0c;從1開始不斷地加一并把這個數寫在黑板上&#xff0c;如果寫上的數字和之前的數字滿足   (ab*b)%k0或者(ba*a)%k0就在他們之間連一條線&#xff0c;如果黑板上出現環就結束…

利用Python隨機或暴力生成密碼

""" Title: python 密碼生成 Author: JackieZheng Date: 2022-04-09 12:47:33 LastEditTime: 2022-04-09 14:00:46 LastEditors: Please set LastEditors Description: FilePath: \\pythonCode\\python_pwd_generater.py """import itertools im…

EasyNetQ-用于使用 RabbitMQ 的 .NET API開源的工具庫

Part1介紹EasyNetQ 的目標是提供一個庫&#xff0c;用于在 .NET 中使用 RabbitMQ 盡可能簡單。為了做到這一點&#xff0c;它通過強制執行一些簡單的約定來以靈活性換取簡單性。這些包括&#xff1a;消息應該由 .NET 類型表示。消息應按其 .NET 類型進行路由。這意味著消息是由…

python 中 __name__ 的使用

1. 如果模塊是被導入&#xff0c;__name__的值為模塊名字2. 如果模塊是被直接執行&#xff0c;__name__的值為’__main__’Py1.py #&#xff01;/usr/bin/env python def test():print __name__ ,__name__ if __name__ __main__:test() Py2.py #&#xff01;/usr/bin/env pyt…

第6章 循環

6.1 range 函數用來創建一個數字列表&#xff0c;它的范圍是從起始數字開始到結束數字之前 1 >>> for x in range(0,5): 2 print(Hello %s % x) 3 4 Hello 0 5 Hello 1 6 Hello 2 7 Hello 3 8 Hello 4 1 >>> print(list(range(10,20))) 2 [10, 11, 12, …

C# 實例解釋面向對象編程中的依賴反轉原則

在面向對象編程中&#xff0c;SOLID 是五個設計原則的首字母縮寫&#xff0c;旨在使軟件設計更易于理解、靈活和可維護。這些原則是由美國軟件工程師和講師羅伯特C馬丁(Robert Cecil Martin)提出的許多原則的子集&#xff0c;在他2000年的論文《設計原則與設計模式》中首次提出…

Linux學習筆記之一————什么是Linux及其應用領域

1.1認識Linux 1&#xff09;什么是操作系統 2&#xff09;現實生活中的操作系統 win7 Mac Android iOS 3&#xff09; 操作系統的發展史 &#xff08;1&#xff09;Unix 1965年之前的時候&#xff0c;電腦并不像現在一樣普遍&#xff0c;它可不是一般人能碰的起的&#xff0c;…

Flex中寬度計算

flex 有三個屬性值&#xff0c;分別是 flex-grow&#xff0c; flex-shrink&#xff0c; flex-basis&#xff0c;默認值是 0 1 auto。 發現網上詳細介紹他們的文章比較少&#xff0c; 今天就詳細說說他們&#xff0c;先一個一個看。 flex-grow 定義項目的放大比例&#xff0c;默…

Lucene詳解

一.lucene原理 Lucene 是apache軟件基金會一個開放源代碼的全文檢索引擎工具包&#xff0c;是一個全文檢索引擎的架構&#xff0c;提供了完整的查詢引擎和索引引擎&#xff0c;部分文本分析引擎。它不是一個完整的搜索應用程序&#xff0c;而是為你的應用程序提供索引和搜索功能…

.NET 6.0中使用Identity框架實現JWT身份認證與授權

原文作者&#xff1a;Sarathlal Saseendran原文鏈接&#xff1a;https://www.c-sharpcorner.com/article/jwt-authentication-and-authorization-in-net-6-0-with-identity-framework/翻譯&#xff1a;沙漠盡頭的狼&#xff08;谷歌翻譯加持&#xff09;介紹微軟于 2021 年 11 …

adb devices 里面有很多 emulator-XXXX的解決方法

2019獨角獸企業重金招聘Python工程師標準>>> adb kill-server 轉載于:https://my.oschina.net/sfshine/blog/700354

MQ(Message Queue)簡介

一、何為MQ&#xff1f; MQ全稱為Message Queue, 消息隊列&#xff08;MQ&#xff09;是一種應用程序對應用程序的通信方法。應用程序通過讀寫出入隊列的消息&#xff08;針對應用程序的數據&#xff09;來通信&#xff0c;而無需專用連接來鏈接它們。消息傳遞指的是程序之間通…

【GlobalMapper精品教程】015:矢量面圖層的創建及數字化操作

本文講解在Globalmapper中文23.0中創建矢量面狀數據(政區數據),并進行面狀數據采集及編輯的詳細操作流程,數據為配套案例數據包中的data015.rar。 參考閱讀: ArcGIS實驗教程——實驗三:矢量數據采集與編輯(矢量化) 文章目錄 一、認識工具條1. 數字化(創建)工具條2. 選…

Blazor University (39)JavaScript 互操作 —— 更新 document title

原文鏈接&#xff1a;https://blazor-university.com/javascript-interop/calling-javascript-from-dotnet/updating-the-document-title/更新 document title源代碼[1]在創建 Blazor 布局[2]部分中&#xff0c;我們看到了 Blazor 應用程序如何存在于 HTML&#xff08;或 cshtm…

IIS 日志文件位置

IIS 6 Log files location IIS 6中日志文件的位置%windir%\System32\LogFilesIIS 7 Log files location IIS的日志文件的位置%SystemDrive%\inetpub\logs\LogFiles用戶每打開一次網頁&#xff0c;iis 都會記錄用戶IP、訪問的網頁地址、訪問時間、訪問狀態等信息&#xff0c;這些…

APP測試流程和測試點

1 APP測試基本流程 1.1流程圖 1.2測試周期 測試周期可按項目的開發周期來確定測試時間&#xff0c;一般測試時間為兩三周&#xff08;即15個工作日&#xff09;&#xff0c;根據項目情況以及版本質量可適當縮短或延長測試時間。正式測試前先向主管確認項目排期。 1.3測試資源 測…

39所強基計劃試點高校已全部公布招生簡章

截至目前(4月8日下午) 39所強基計劃試點高校 已全部公布招生簡章 各高校招生要求是什么&#xff1f; 招生專業有哪些&#xff1f; 什么時候報名&#xff1f; 一起來看 北京大學 招生對象及報名條件 各省&#xff08;區、市&#xff09;符合2022年全國普通高等學校招生統…

【ArcGIS錯誤異常100問】之001:License服務無法啟動權威解決辦法

測試環境&#xff1a; 操作系統&#xff1a;Windows10ArcGIS版本&#xff1a;10.X結果&#xff1a;通過測試 文章目錄1. 錯誤提示2. 問題分析3. 解決辦法3.1 關閉Windows Defender3.2 關閉系統防火墻3.3 刪除邁克菲&#xff08;McAfee&#xff09;殺毒軟件3.4 在系統服務中啟動…

Appium wait等待的三種方法

1、sleep()方法Thread.sleep&#xff08;60000&#xff09;強制等待60s2、隱式等待implicitlyWait()driver.manage().timeouts().implicitlyWait(30,TimeUnit.SECONDS);全局等待30s不管元素是否已經加載1) 當使用了隱式等待執行測試的時候&#xff0c;如果WebDriver沒有在DOM中…

ASP.NET Core 技術內幕與項目實戰讀后感

前幾天拿到了楊中科老師的新書《ASP.NET Core 技術內幕與項目實戰》&#xff0c;迫不及待的“兩”口氣讀完了。用一句話來總結&#xff0c;這是一本寫給.NET開發者的非常實用的接地氣的好書&#xff0c;感覺有必要自發為這本書宣傳一波。楊老師在 .NET 開發者社區中的知名度非常…