摘要:文章列出解決方案以及對應(yīng)的,拒絕說概念,不在稀里糊涂。服務(wù)器據(jù)此決定,該實際請求是否被允許。在有效時間內(nèi),瀏覽器無須為同一請求再次發(fā)起預(yù)檢請求。請注意,瀏覽器自身維護了一個最大有效時間,如果該首部字段的值超過了最大有效時間,將不會生效。
文章列出解決方案以及對應(yīng)的demo, 拒絕說概念,不在稀里糊涂。什么情況出現(xiàn)跨域?
協(xié)議不同
域名不同
端口不同
跨域解決方案 1.同一個主域下不同子域之間的跨域請求 - document.domain+iframe同一個 origin 下,父頁面可以通過 iframe.contentWindow 直接訪問 iframe 的全局變量、DOM 樹等,iframe 可以也通過 parent/top 對父頁面做同樣的事情。
domain.html
domain2.html
2222222222
完整demo
2. 完全不同源 - postMessagehtml5新增API, 支持IE8+。
otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow 其他窗口的一個引用,比如iframe的contentWindow屬性、執(zhí)行window.open返回的窗口對象、或者是命名過或數(shù)值索引的window.frames。
message 將要發(fā)送到其他 window的數(shù)據(jù)
targetOrigin 通過窗口的origin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串""(表示無限制)或者一個URI。如果你明確的知道消息應(yīng)該發(fā)送到哪個窗口,那么請始終提供一個有確切值的targetOrigin,而不是。不提供確切的目標將導(dǎo)致數(shù)據(jù)泄露到任何對數(shù)據(jù)感興趣的惡意站點。
transfer 可選 是一串和message 同時傳遞的 Transferable 對象. 這些對象的所有權(quán)將被轉(zhuǎn)移給消息的接收方,而發(fā)送一方將不再保有所有權(quán)。
傳遞過來的message的屬性有:
data 從其他 window 中傳遞過來的對象。
origin 調(diào)用 postMessage 時消息發(fā)送方窗口的 origin . 這個字符串由 協(xié)議、“://“、域名、“ : 端口號”拼接而成
source 對發(fā)送消息的窗口對象的引用; 您可以使用此來在具有不同origin的兩個窗口之間建立雙向通信
下面index.html和index2.html通信
index.html
index2.html
子窗口
完整demo
3. 完全不同源 - location.hash+iframe原理是利用location.hash來進行傳值。改變hash并不會導(dǎo)致頁面刷新,所以可以利用hash值來進行數(shù)據(jù)傳遞,當然數(shù)據(jù)容量是有限的。
例如:假設(shè)a.tblog.com:3004 和 192.168.101.5:3004/index2.html通信
原理:a.tblog.com:3004中index.html以iframe將192.168.101.5:3004/index2.html頁面引入,在192.168.101.5:3004/index2.html中插入新的iframe, 此iframe引入的頁面和a.tblog.com:3004同源,就可將192.168.101.5:3004/index2.html的hash數(shù)據(jù)傳入a.tblog.com:3004頁面的hash值中。parent.parent.location.hash = self.location.hash.substring(1);
a.tblog.com:3004/index.html
192.168.101.5:3004/ index2.html
a.tblog.com:3004/index3.html
完整demo
4. window.name + iframe 跨域window.name 獲取/設(shè)置窗口的名稱。
窗口的名字主要用于為超鏈接和表單設(shè)置目標(targets)。窗口不需要有名稱。
window.name屬性可設(shè)置或者返回存放窗口名稱的一個字符串, name值在不同頁面或者不同域下加載后依舊存在,沒有修改就不會發(fā)生變化,并且可以存儲非常長的name(2MB)。
場景1 - 同源
a.html
b.html
場景2 - 不同源
利用iframe中window.name在不同頁面或者不同域下加載后依舊存在的特性。
a.tblog.com:3004/a.html中通過iframe添加192.168.0.103:3004/b.html(數(shù)據(jù)頁面, 指定window.name 的值),監(jiān)聽iframe的load, 改變iframe的src與a.tblog.com:3004/a.html同源代理頁面a.tblog.com:3004/c.html(空頁面)。
a.tblog.com:3004/a.html
const iframe = document.createElement("iframe"); iframe.style.display = "none"; let state = 0; iframe.onload = function () { console.log("iframe.onload", state, iframe.contentWindow); if (state === 1) { const data = JSON.parse(iframe.contentWindow.name); console.log(data, state); iframe.contentWindow.document.write(""); iframe.contentWindow.close(); document.body.removeChild(iframe); } else if (state === 0) { state = 1; console.log("數(shù)據(jù)", window.name) iframe.contentWindow.location = "http://a.tblog.com:3004/c.html"; } }; iframe.src = "http://192.168.0.103:3004/b.html"; document.body.appendChild(iframe);
完整demo
5. 跨域jsonpjsonp原理:
首先是利用script標簽的src屬性來實現(xiàn)跨域。
客戶端注冊callback方法名,攜帶在URL上, 如"http://127.0.0.1:8080/getNews?callback=getData"
服務(wù)器響應(yīng)后生成json, 將json放在剛才接收到的callback的函數(shù)中,就生成一段getData(json)
客戶端瀏覽器將script 標簽插入 DOM,解析script標簽后,會執(zhí)行g(shù)etData(json)。
由于使用script標簽的src屬性,因此只支持get方法
客戶端代碼
服務(wù)端代碼
const http = require("http"); const fs = require("fs"); const path = require("path"); const url = require("url"); http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); switch(pathObj.pathname){ case "/getNews": const news = [{id: 678}]; res.setHeader("Content-type", "text/json; charset=utf-8"); if(pathObj.query.callback){ res.end(pathObj.query.callback + "(" + JSON.stringify(news) + ")"); }else { res.end(JSON.stringify(news)); } break; default: res.writeHead(404, "not found"); } }).listen(8080);
完整demo
6. CORS跨域原理
跨域資源共享(CORS) 是一種機制,它使用額外的 HTTP 頭來告訴瀏覽器 讓運行在一個 origin (domain) 上的Web應(yīng)用被準許訪問來自不同源服務(wù)器上的指定的資源。跨域資源共享( CORS )機制允許 Web 應(yīng)用服務(wù)器進行跨域訪問控制,從而使跨域數(shù)據(jù)傳輸?shù)靡园踩M行。
什么情況下需要CORS
前文提到的由 XMLHttpRequest 或 Fetch 發(fā)起的跨域 HTTP 請求。
Web 字體 (CSS 中通過 @font-face 使用跨域字體資源), 因此,網(wǎng)站就可以發(fā)布 TrueType 字體資源,并只允許已授權(quán)網(wǎng)站進行跨站調(diào)用。
WebGL 貼圖
使用 drawImage 將 Images/video 畫面繪制到 canvas
樣式表(使用 CSSOM)
功能概述
跨域資源共享標準新增了一組 HTTP 首部字段,允許服務(wù)器聲明哪些源站通過瀏覽器有權(quán)限訪問哪些資源。允許服務(wù)器聲明哪些源站通過瀏覽器有權(quán)限訪問哪些資源。對于get以外的請求,瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個預(yù)檢請求(preflight request),從而獲知服務(wù)端是否允許該跨域請求。服務(wù)器確認允許之后,才發(fā)起實際的 HTTP 請求。 真?zhèn)€過程瀏覽器自動完成,服務(wù)器會添加一些附加的頭信息, 因此,實現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實現(xiàn)了CORS接口,就可以跨源通信。
簡單請求
某些請求不會觸發(fā) CORS 預(yù)檢請求。本文稱這樣的請求為“簡單請求”,請注意,該術(shù)語并不屬于 Fetch (其中定義了 CORS)規(guī)范。只要同時滿足以下兩大條件,就屬于簡單請求:
(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
請求響應(yīng)結(jié)果多出的字段:
Access-Control-Allow-Origin: http://api.bob.com Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: FooBar Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin
Access-Control-Allow-Origin:
Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true; 當瀏覽器的credentials設(shè)置為true時, 此響應(yīng)頭表示是否允許瀏覽器讀取response的內(nèi)容,返回true則可以,其他值均不可以,Credentials可以是 cookies, authorization headers 或 TLS client certificates。
Access-Control-Allow-Credentials 頭 工作中與XMLHttpRequest.withCredentials 或Fetch API中的Request() 構(gòu)造器中的credentials 選項結(jié)合使用。Credentials必須在前后端都被配置(即the Access-Control-Allow-Credentials header 和 XHR 或Fetch request中都要配置)才能使帶credentials的CORS請求成功。 如果withCredentials 為false,服務(wù)器同意發(fā)送Cookie,瀏覽器也不會發(fā)送,或者,服務(wù)器要求設(shè)置Cookie,瀏覽器也不會處理。
需要注意的是,如果要發(fā)送Cookie,Access-Control-Allow-Origin就不能設(shè)為星號,必須指定明確的、與請求網(wǎng)頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務(wù)器域名設(shè)置的Cookie才會上傳,其他域名的Cookie并不會上傳,且(跨源)原網(wǎng)頁代碼中的document.cookie也無法讀取服務(wù)器域名下的Cookie。
// 允許credentials: Access-Control-Allow-Credentials: true // 使用帶credentials的 XHR : var xhr = new XMLHttpRequest(); xhr.open("GET", "http://example.com/", true); xhr.withCredentials = true; xhr.send(null); // 使用帶credentials的 Fetch : fetch(url, { credentials: "include" })
Access-Control-Expose-Headers
在跨域訪問時,XMLHttpRequest對象的getResponseHeader()方法只能拿到一些最基本的響應(yīng)頭,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma, 如果想拿到其他字段,就必須在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader("FooBar")可以返回FooBar字段的值。
代碼如下:
http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); switch(pathObj.pathname){ case "/user": const news = [{id: 678}]; res.setHeader("Content-type", "text/json; charset=utf-8"); res.setHeader("Access-Control-Allow-Origin", req.headers.origin); // res.setHeader("Access-Control-Allow-Origin", "*"); // 需要cookie等憑證是必須 res.setHeader("Access-Control-Allow-Credentials", true); res.end(JSON.stringify(news)); break; default: res.writeHead(404, "not found"); } }).listen(8080, (err) => { if (!err) { console.log("8080已啟動"); } });
完整demo
非簡單請求
非簡單請求的CORS請求,會在正式通信之前,增加一次HTTP查詢請求,稱為"預(yù)檢"請求(preflight)。
以獲知服務(wù)器是否允許該實際請求。"預(yù)檢請求“的使用,可以避免跨域請求對服務(wù)器的用戶數(shù)據(jù)產(chǎn)生未預(yù)期的影響。
當請求滿足下述任一條件時,即應(yīng)首先發(fā)送預(yù)檢請求:
使用了下面任一 HTTP 方法:
put
delete
connect
OPTIONS
trace
patch
人為設(shè)置了對cors安全首部字段集合外的其他首部字段, 該集合為:
Accept
Accept-Language
Content-Language
Content-Type
DPR
Downlink
Save-data
Viewport-Width
Width
Content-Type的值不屬于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
請求中的XMLHttpRequestUpload 對象注冊了任意多個事件監(jiān)聽器。
請求中使用了ReadableStream對象。
如下是一個需要執(zhí)行預(yù)檢請求的 HTTP 請求:
http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); switch(pathObj.pathname){ case "/user": const news = {id: 678}; res.setHeader("Content-type", "text/json; charset=utf-8"); res.setHeader("Access-Control-Allow-Origin", req.headers.origin); // res.setHeader("Access-Control-Allow-Origin", "*"); // 需要cookie等憑證是必須 res.setHeader("Access-Control-Allow-Credentials", true); res.end(JSON.stringify(news)); break; default: res.writeHead(404, "not found"); } }).listen(8080, (err) => { if (!err) { console.log("8080已啟動"); } });
瀏覽器請求結(jié)果:
cors2.html:1 Access to XMLHttpRequest at "http://localhost:8080/user" from origin "http://127.0.0.1:3004" has been blocked by CORS policy: Request header field x-pingother is not allowed by Access-Control-Allow-Headers in preflight response.
如圖所示發(fā)起了預(yù)檢請求,請求頭部多了兩個字段:
Access-Control-Request-Method: POST; // 該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法. Access-Control-Request-Headers: Content-Type, X-PINGOTHER; 告知服務(wù)器,實際請求將攜帶兩個自定義請求首部字段:X-PINGOTHER 與 Content-Type。服務(wù)器據(jù)此決定,該實際請求是否被允許。
上例需要成功響應(yīng)數(shù)據(jù),服務(wù)端需要同意:
http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); switch(pathObj.pathname){ case "/user": const news = {id: 678}; res.setHeader("Content-type", "text/json; charset=utf-8"); res.setHeader("Access-Control-Allow-Origin", req.headers.origin); // 新增的 res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); res.setHeader("Access-Control-Allow-Headers", "X-PINGOTHER, Content-Type"); res.setHeader("Access-Control-Max-Age", 86400); res.end(JSON.stringify(news)); break; default: res.writeHead(404, "not found"); } }).listen(8080, (err) => { if (!err) { console.log("8080已啟動"); } });
服務(wù)段新增的字段:
Access-Control-Allow-Origin: req.headers.origin Access-Control-Allow-Methods: POST, GET, OPTIONS // 表明服務(wù)器允許客戶端使用 POST, GET 和 OPTIONS 方法發(fā)起請求。該字段與 HTTP/1.1 Allow: response header 類似,但僅限于在需要訪問控制的場景中使用。這是為了避免多次"預(yù)檢"請求。 Access-Control-Allow-Headers: X-PINGOTHER, Content-Type // 如果瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,表明服務(wù)器支持的所有頭信息字段,不限于瀏覽器在"預(yù)檢"中請求的字段。 Access-Control-Max-Age: 86400 // 表明該響應(yīng)的有效時間為 86400 秒,也就是 24 小時。在有效時間內(nèi),瀏覽器無須為同一請求再次發(fā)起預(yù)檢請求。請注意,瀏覽器自身維護了一個最大有效時間,如果該首部字段的值超過了最大有效時間,將不會生效。7. nodejs代理跨域
node中間件實現(xiàn)跨域代理,是通過一個代理服務(wù)器,實現(xiàn)數(shù)據(jù)的轉(zhuǎn)發(fā),也可以通過設(shè)置cookieDomainRewrite參數(shù)修改響應(yīng)頭中cookie中域名,實現(xiàn)當前域的cookie寫入,方便接口登陸認證。
原理:服務(wù)器之間數(shù)據(jù)請求不存在跨域限制(同源策略是瀏覽器行為), 所以先將請求代理到代理服務(wù)器, 代理服務(wù)器在內(nèi)部請求真實的服務(wù)器得到結(jié)果后end連接。
http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); console.log("server", pathObj.pathname) switch(pathObj.pathname){ case "/user": const news = {id: 678}; res.end(JSON.stringify(news)); break; default: res.setHeader("Content-type", "text/json; charset=utf-8"); res.end("未知錯誤"); } }).listen(4000, (err) => { if (!err) { console.log("4000已啟動"); } }); http.createServer(function(req, res){ const pathObj = url.parse(req.url, true); switch(pathObj.pathname){ case "/user": res.setHeader("Content-type", "text/json; charset=utf-8"); res.writeHead(200, { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "POST, GET, OPTIONS", "Access-Control-Allow-Headers": "X-PINGOTHER, Content-Type", }); console.log("proxy", req.method, pathObj.pathname); // 請求真實服務(wù)器 const proxyRequest = http.request({ host: "127.0.0.1", port: 4000, url: "/", path: pathObj.pathname, method: req.method, headers: req.headers }, (proxyRes) => { let body = ""; proxyRes.on("data", (chunk) => { body += chunk; }); proxyRes.on("end", () => { console.log("響應(yīng)的數(shù)據(jù) " + body ); res.end(body); }) }).end(); break; default: res.writeHead(404, "not found"); res.end(body); break; } }).listen(8080, (err) => { if (!err) { console.log("8080已啟動"); } });
注意:
服務(wù)器和瀏覽器數(shù)據(jù)交互也需要遵循同源策略
-- 持續(xù)更新 --
Tips:
代碼地址。~ github
WeChat
參考文章
https://developer.mozilla.org...
http://vinc.top/2017/02/09/%E...
http://www.ruanyifeng.com/blo...
https://segmentfault.com/a/11...
https://developer.mozilla.org...
https://developer.mozilla.org...
http://www.ruanyifeng.com/blo...
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/103925.html
摘要:瀏覽器的同源策略瀏覽器所遵守的同源策略是指限制不同源之間執(zhí)行特定操作。這正是同源策略想要規(guī)避的安全隱患。目前為止,你已經(jīng)充分了解同源策略這個主題。 我們之前提到過,AJAX技術(shù)使開發(fā)者能夠?qū)W⒂诨ヂ?lián)網(wǎng)中數(shù)據(jù)的傳輸,而不再拘泥于數(shù)據(jù)傳輸?shù)妮d體。通過AJAX技術(shù),我們獲取數(shù)據(jù)的方式變得更加靈活,可控和優(yōu)雅。 但是AJAX技術(shù)并不是一把萬能鑰匙,互聯(lián)網(wǎng)中的數(shù)據(jù)隱私和數(shù)據(jù)安全(例如你的銀行賬號...
摘要:對的請求,也是要有一個了解,比如協(xié)議,請求方式,請求過程,結(jié)果狀態(tài)碼等。教程協(xié)議詳解經(jīng)典面試題一個故事講完響應(yīng)狀態(tài)碼上面提到響應(yīng)狀態(tài)碼,在這里也簡單寫下。 勸了別人無數(shù)次,讓別人喝了雞湯,幫別人填坑,自己卻掉了坑 1.前言 在前端學(xué)習(xí)里面,很多人都是注重學(xué)習(xí)代碼(html,css,js)。或者是一些框架,庫(jquery,vue,react),或者是各種工具(webpack,gulp)...
摘要:對的請求,也是要有一個了解,比如協(xié)議,請求方式,請求過程,結(jié)果狀態(tài)碼等。教程協(xié)議詳解經(jīng)典面試題一個故事講完響應(yīng)狀態(tài)碼上面提到響應(yīng)狀態(tài)碼,在這里也簡單寫下。 勸了別人無數(shù)次,讓別人喝了雞湯,幫別人填坑,自己卻掉了坑 1.前言 在前端學(xué)習(xí)里面,很多人都是注重學(xué)習(xí)代碼(html,css,js)。或者是一些框架,庫(jquery,vue,react),或者是各種工具(webpack,gulp)...
摘要:對的請求,也是要有一個了解,比如協(xié)議,請求方式,請求過程,結(jié)果狀態(tài)碼等。教程協(xié)議詳解經(jīng)典面試題一個故事講完響應(yīng)狀態(tài)碼上面提到響應(yīng)狀態(tài)碼,在這里也簡單寫下。 勸了別人無數(shù)次,讓別人喝了雞湯,幫別人填坑,自己卻掉了坑 1.前言 在前端學(xué)習(xí)里面,很多人都是注重學(xué)習(xí)代碼(html,css,js)。或者是一些框架,庫(jquery,vue,react),或者是各種工具(webpack,gulp)...
摘要:前言最近在業(yè)務(wù)代碼中深受跨域問題困擾,因此特別寫一篇博客來記錄一下自己對跨域的理解以及使用到的參考資料。內(nèi)嵌式跨域通常也是允許的。而我使用時因為這個響應(yīng)報文最后被認為是跨域問題,無法從中獲得的狀態(tài)碼。它代表服務(wù)器支持跨域時攜帶認證信息。 前言 最近在業(yè)務(wù)代碼中深受跨域問題困擾,因此特別寫一篇博客來記錄一下自己對跨域的理解以及使用到的參考資料。本文的項目背景基于vue+vuex+axio...
閱讀 1061·2023-04-26 02:02
閱讀 2401·2021-09-26 10:11
閱讀 3553·2019-08-30 13:10
閱讀 3743·2019-08-29 17:12
閱讀 720·2019-08-29 14:20
閱讀 2187·2019-08-28 18:19
閱讀 2230·2019-08-26 13:52
閱讀 954·2019-08-26 13:43