由跨域引發一些思考
- 前言
- 什么是跨域?
- 為什么會產生跨域?
- 跨域場景示例:
- 跨域常見的解決方法:
- JSONP(JSON with Padding)
- CORS(Cross-Origin Resource Sharing)
- document.domain + iframe
- location.hash + iframe
- window.name + iframe
- postMessage
- 代理服務器
- Node.js中間件代理
- WebSocket
- 什么是代理?
- 工作原理
- 功能與應用
- 類型
- 為什么說配置代理能夠解決跨域的問題呢?
- 基于Webpack或者是Vite開發配置代理?
- 本地開發配置了代理,上線后還需要配置代理么?
- 小結
前言
最近加了個群,今天有人在群里問了一個問題“前端配置了跨域代理,項目發布到線上還需要配置 nginx 反向代理嗎?”,由此,在群里展開了激烈的討論。討論的主要點就是在跨域和代理上面,由此引發了我的一些思考。首先記錄一些基本的概念。
什么是跨域?
跨域(Cross-Origin)是指在互聯網上的一個域下的文檔或腳本嘗試請求另一個域下的資源時,域名、協議或端口不同的這種行為。由于瀏覽器實施的同源策略(Same-origin policy),這是一種安全機制,用于限制一個源中的文檔或腳本與來自另一個源的資源進行交互,以防止惡意網站讀取另一個網站的數據。
為什么會產生跨域?
- 同源策略:瀏覽器的同源策略是為了保護用戶信息的安全,它規定了來自一個源的Web內容,只能與同一個源下的資源進行交互。這意味著,如果嘗試從不同源加載數據,如使用XMLHttpRequest請求數據,瀏覽器會默認阻止這種行為,以防惡意站點讀取另一個站點的數據。
- 安全考量:如果沒有同源策略,惡意網站可以通過腳本讀取銀行網站的cookie信息等敏感數據,造成嚴重的安全威脅。因此,跨域限制是必要的安全措施。
跨域場景示例:
- 當前端網頁部署在example.com域名下,嘗試通過JavaScript向api.example2.com發送Ajax請求獲取數據時,就發生了跨域。
- 即使是同一域名,但端口不同(如example.com:80嘗試訪問example.com:8080)或者協議不同(HTTPS與HTTP),也會被視為跨域。
跨域常見的解決方法:
JSONP(JSON with Padding)
- 原理:利用
<script>
標簽沒有跨域限制的特性,通過動態創建<script>
標簽,并設置其src屬性為跨域URL(通常包含回調函數名作為參數),來加載并執行跨域腳本。 - 缺點:只能發送GET請求,存在安全風險(如XSS攻擊)。
CORS(Cross-Origin Resource Sharing)
- 原理:通過服務器設置響應頭中的Access-Control-Allow-Origin等字段來允許跨域請求。
- 支持所有類型的HTTP請求。
- 需要在服務器端進行配置。
document.domain + iframe
- 原理:將兩個頁面的document.domain設置為相同的值,以允許它們通過iframe進行跨域通信。
- 僅限于主域相同,子域不同的跨域應用場景。
location.hash + iframe
- 原理:利用iframe的location.hash屬性在不同域之間進行數據傳遞。
- 需要借助中間頁面來實現雙向通信。
window.name + iframe
- 原理:通過iframe的window.name屬性在不同域之間共享數據。
- window.name的值在頁面重新加載后依然存在。
postMessage
- 原理:使用window.postMessage方法可以在不同的窗口(包括iframe)之間安全地傳遞數據。
- 需要目標窗口監聽message事件來接收數據。
代理服務器
- 原理:在服務器端設置一個代理服務器,作為中間層來處理跨域請求。
- 客戶端向代理服務器發送請求,代理服務器將請求轉發給目標服務器,并將響應返回給客戶端。
- 可以在前端完全避免跨域問題,但增加了服務器端的復雜性。
Node.js中間件代理
- 原理:在Node.js服務器端使用中間件(如http-proxy-middleware)來代理跨域請求。
- 客戶端向Node.js服務器發送請求,Node.js服務器將請求轉發給目標服務器,并將響應返回給客戶端。
- 可以在前端避免跨域問題,但需要搭建Node.js服務器環境。
WebSocket
- 原理:WebSocket是一種網絡通信協議,可以在單個TCP連接上進行全雙工通信。
- 不受同源策略的限制,可以實現跨域通信。
- 需要服務器端支持WebSocket協議。
什么是代理?
這里的代理其實是指的網絡代理。網絡代理(Proxy),又稱代理服務器,是一種位于客戶端和目標服務器之間的中間服務器。以下是關于網絡代理的詳細解釋:
網絡代理允許一個網絡終端(一般為客戶端)通過這個服務與另一個網絡終端(一般為服務器)進行非直接的連接。簡單來說,它就是網絡信息的中轉站,代理網絡用戶去取得網絡信息。
工作原理
- 客戶端需要配置代理服務器的地址和端口,然后將網絡請求發送給代理服務器。
- 代理服務器接收請求后,根據客戶端的要求進行處理(如更改IP地址、緩存資源等),然后將請求轉發給目標服務器。
- 目標服務器將響應發送給代理服務器,代理服務器再將響應轉發給客戶端。
功能與應用
- 隱私保護:通過使用匿名代理或VPN,用戶可以隱藏自己的真實IP地址,防止被目標服務器和網絡監聽者識別,有助于保護個人隱私和防止網絡監控。
- 繞過地域限制:有些網絡資源會因為地域、政策等原因進行限制。通過使用代理服務器或VPN,用戶可以“偽裝”成其他地區的IP地址,從而訪問受限制的資源。
- 負載均衡:對于大型網站和應用,負載均衡是保證高性能和可用性的關鍵。通過使用反向代理,可以將客戶端的請求分發到多個目標服務器,從而實現負載均衡和故障轉移。
- 內容過濾與審計:企業和學校等組織可以使用HTTP代理對員工和學生的網絡訪問進行監控和控制,如限制訪問特定網站、屏蔽敏感內容等,有助于保證網絡安全和合規性。
- 網絡加速:通過使用緩存代理和CDN技術,可以將網絡資源緩存到距離用戶更近的服務器上,從而提高訪問速度和降低延遲。
類型
- HTTP代理:能夠代理客戶機的HTTP訪問,主要是代理瀏覽器訪問網頁。
- FTP代理:能夠代理客戶機上的FTP軟件訪問FTP服務器。
- RTSP代理:代理客戶機上的Realplayer訪問Real流媒體服務器。
- POP3代理:代理客戶機上的郵件軟件用POP3方式收發郵件。
- VPN代理:在共用網絡上建立專用網絡的技術,通過在公用網絡服務商ISP所提供的網絡平臺之上的邏輯網絡來實現。
為什么說配置代理能夠解決跨域的問題呢?
配置代理能夠解決跨域問題,主要是因為代理服務器在請求發送和接收過程中充當了中間層的角色。以下是詳細解釋:
中間層的作用:
- 當客戶端發送請求時,請求首先到達代理服務器,而不是直接到達目標服務器。
- 在代理服務器上,可以將請求中的域名或IP地址進行轉換,使得原本不同源的請求在服務器端看起來像是來自同一源的請求。
解決跨域的原理: - 跨域問題本質上是瀏覽器的一種安全策略,即同源策略(Same-Origin Policy),它禁止不同源的網頁之間進行某些交互。
- 通過配置代理,可以在請求發送之前將請求中的域名轉換為目標服務器所接受的域名,從而繞過瀏覽器的同源策略檢查。
- 例如,如果客戶端在localhost:8080下運行,而目標服務器在www.example.com上,客戶端發送的請求會首先到達代理服務器。在代理服務器上,可以將請求的域名從localhost:8080轉換為www.example.com,然后再將請求轉發給目標服務器。
返回數據的處理: - 當目標服務器返回數據時,數據也會首先到達代理服務器。
- 在代理服務器上,可以將返回數據中的域名從www.example.com轉換回localhost:8080(或其他客戶端所期望的域名),然后再將數據返回給客戶端。
基于Webpack或者是Vite開發配置代理?
我們在平時的前端開發的過程中,通常都會配置代理來解決跨域問題;比如說在Webpack中配置代理:
const { createProxyMiddleware } = require('http-proxy-middleware'); module.exports = { // ... 其他配置項 devServer: { proxy: { '/api': { target: '<url>', // 目標服務器的地址 ws: true, // 如果你的應用程序使用了websockets,則需要設置為true secure: false, // 如果你的目標服務器使用了HTTPS,需要設置為false(除非你配置了HTTPS代理) changeOrigin: true, // 如果你的目標服務器和你的開發服務器不在同一個域上,需要設置為true pathRewrite: {'^/api' : ''} // 重寫路徑,例如將/api/users重寫為/users }, // 可以配置多個代理規則 // '/other': { ... } } }
};
在Vite中配置代理:
// vite.config.js
export default { // ... 其他配置項 server: { proxy: { // 選項寫法 '/api': { target: '<url>', // 目標服務器的地址 changeOrigin: true, // 如果你的目標服務器和你的開發服務器不在同一個域上,需要設置為true rewrite: (path) => path.replace(/^\/api/, '') // 重寫路徑,例如將/api/users重寫為/users }, // 或者使用字符串簡寫 '/foo': '<other_url>' } }
}
本地開發配置了代理,上線后還需要配置代理么?
在本地開發環境中配置代理主要是為了解決跨域問題,允許前端代碼能夠訪問不同源的API服務。然而,當應用程序部署到生產環境后,跨域問題的處理方式會有所不同。
通常,上線后不需要在前端應用中配置代理。 這是因為生產環境中的前端代碼通常是通過構建過程(如Webpack、Rollup、Vite等)打包并部署到靜態文件服務器或CDN上的,而這些服務器通常不會運行代理服務器。
在生產環境中處理跨域問題的方法主要有以下幾種:
- 后端服務器配置CORS(跨源資源共享):
這是最常見的方法。后端服務器配置CORS策略,允許來自特定來源(或所有來源)的跨域請求。前端代碼不需要進行任何特殊配置,只需按照正常的HTTP請求方式發送請求即可。 - 使用API網關:
在微服務架構中,API網關通常用于處理跨域請求。API網關可以配置CORS策略,并將請求轉發到后端服務。這樣,前端代碼就可以通過API網關訪問后端服務,而無需直接配置代理。 - JSONP(僅適用于GET請求):
雖然JSONP是一種較老的跨域技術,但它仍然在某些情況下被使用。它通過動態插入
小結
在我們前端開發過程中配置了前端代理,當項目上線后,要根據實際的策略來考慮是否需要再遠程再配置代理