處理前端跨域方式
爲什麼要設置跨域
因爲瀏覽器的同源策略導致了跨域。
同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。
同源的定義
URL | 結果 | 原因 |
---|---|---|
http://store.company.com/dir2/other.html | 成功 | 只有路徑不同 |
http://store.company.com/dir/inner/another.html | 成功 | 只有路徑不同 |
https://store.company.com/secure.html | 失敗 | 不同協議 ( https和http ) |
http://store.company.com:81/dir/etc.html | 失敗 | 不同端口 ( http:// 80是默認的) |
http://news.company.com/dir/other.html | 失敗 | 不同域名 ( news和store ) |
解決方案
CORS<跨域資源共享>
CORS 需要瀏覽器和後端同時支持。IE 8 和 9 需要通過 XDomainRequest 來實現。
這裏有詳細的介紹跨域資源共享 CORS 詳解
簡單點說:
瀏覽器將CORS請求分成兩類:簡單請求(simple request)和 非簡單請求(not-so-simple request)。
簡單請求
只要同時滿足以下兩大條件
- 請求方法是以下三種方法之一:
- HEAD
- GET
- POST
- HTTP的頭信息不超出以下幾種字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同時滿足上面兩個條件,就屬於非簡單請求。
瀏覽器對這兩種請求的處理,是不一樣的。
解決方法
/**允許所有的地址訪問, 取消跨域攔截 */
res.header("Access-Control-Allow-Origin", "*");
非簡單請求
非簡單請求是那種對服務器有特殊要求的請求,比如請求方法是PUT或DELETE,或者Content-Type字段的類型是application/json,或者增加了一個頭信息字段token
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱爲"預檢"請求(preflight)。
解決方法
/**允許所有的地址訪問, 取消跨域攔截 */
res.header("Access-Control-Allow-Origin", "*");
// #region 複雜請求必須設置 Access-Control-Allow-Methods 和 Access-Control-Allow-Headers
// 其中Access-Control-Allow-Headers中除了基礎的Origin, X-Requested-With, Content-Type, Accept
//如果用戶有自己新增的頭信息則必須加入Access-Control-Allow-Headers
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, ' + "token");
// #endregion
withCredentials cookie信息的傳遞
CORS請求默認不發送Cookie和HTTP認證信息。如果要把Cookie發到服務器,一方面要服務器同意,指定Access-Control-Allow-Credentials字段。
解決方案:
前端
xhr.withCredentials = true; // 設置允許傳遞cookies
服務器端
res.header('Access-Control-Allow-Credentials','true');
jsonP跨域
實現原理:利用 script 標籤沒有跨域限制的漏洞,網頁可以得到從其他來源動態產生的 JSON 數據。JSONP 請求一定需要對方的服務器做支持纔可以。
JSONP優缺點:JSONP 優點是簡單兼容性好,可用於解決主流瀏覽器的跨域數據訪問的問題。缺點是僅支持 get 方法具有侷限性,不安全可能會遭受 XSS 攻擊。
JSONP的實現流程:
- 定義一個全局方法window.<前臺方法> = ()=>{} 例如:window.cb = ()=>{}
- 生成一個script標籤src爲<請求地址>?<後臺方法>=<前臺方法>[例如:http://localhost:9977/a?callback=cb]
- 服務端接受請求 將數據填充至指定格式爲"<前臺方法>(’<數據>’)" 前端接收的相應數據格式應該大致
cb("歡迎來到9977端口,我已經放行所有的地址請求")
- 前端通過window.<前臺方法>獲取數據
換個方法說就好像 後端通過調用前端的方法將數據傳遞進去。
proxy代理跨域
實現原理:同源策略是瀏覽器需要遵循的標準,而如果是服務器向服務器請求就無需遵循同源策略。
代理服務器,需要做以下幾個步驟:
- 接受客戶端請求。
- 將請求轉發給目標服務器。
- 獲取目標服務其響應的數據
- 將響應轉發給客戶端
要點
通過代理跨域的要點是代理服務器必須能遵循同源策略接受客戶端的請求。
WebSocket
實現原理:
WebSocket協議本身不要求同源策略(Same-origin Policy),也就是某個地址爲http://a.com的網頁可以通過WebSocket連接到ws://b.com。
但是,瀏覽器會發送Origin的HTTP頭給服務器,服務器可以根據Origin拒絕這個WebSocket請求。所以,是否要求同源要看服務器端如何檢查
Websocket 是 HTML5 的一個持久化的協議,它實現了瀏覽器與服務器的全雙工通信,同時也是跨域的一種解決方案。
WebSocket 和 HTTP 都是應用層協議,都基於 TCP 協議。
WebSocket 是一種雙向通信協議,在建立連接之後,WebSocket 的 server 與 client 都能主動向對方發送或接收數據。
WebSocket 在建立連接時需要藉助 HTTP 協議,連接建立好了之後 client 與 server 之間的雙向通信就與 HTTP 無關了。
changelog
@TODO