摘要:跨域的解決方案利用標(biāo)簽不受跨域限制而形成的一種方案。跨域資源共享標(biāo)準(zhǔn)新增了一組首部字段,允許服務(wù)器聲明哪些源站有權(quán)限訪問哪些資源。它是基于的全雙工通信即服務(wù)端和客戶端可以雙向進(jìn)行通訊,并且允許跨域通訊。
1.什么是跨域
同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的重要安全機(jī)制。同源指:協(xié)議、域名、端口號(hào)必須一致。
同源策略控制了不同源之間的交互,例如在使用XMLHttpRequest 或 標(biāo)簽時(shí)則會(huì)受到同源策略的約束。這些交互通常分為三類:
通常允許跨域?qū)懖僮鳎–ross-origin writes)。例如鏈接(links),重定向以及表單提交。特定少數(shù)的HTTP請(qǐng)求需要添加 preflight。
通常允許跨域資源嵌入(Cross-origin embedding)。
通常不允許跨域讀操作(Cross-origin reads)。但常可以通過內(nèi)嵌資源來巧妙的進(jìn)行讀取訪問。例如可以讀取嵌入圖片的高度和寬度,調(diào)用內(nèi)嵌腳本的方法,或availability of an embedded resource.
下面為允許跨域資源嵌入的示例,即一些不受同源策略影響的標(biāo)簽示例:
標(biāo)簽嵌入跨域腳本。語法錯(cuò)誤信息只能在同源腳本中捕捉到。
標(biāo)簽嵌入CSS。由于CSS的松散的語法規(guī)則,CSS的跨域需要一個(gè)設(shè)置正確的Content-Type 消息頭。不同瀏覽器有不同的限制: IE, Firefox, Chrome, Safari (跳至CVE-2010-0051)部分 和 Opera。
嵌入圖片。支持的圖片格式包括PNG,JPEG,GIF,BMP,SVG,...
@font-face 引入的字體。一些瀏覽器允許跨域字體( cross-origin fonts),一些需要同源字體(same-origin fonts)。
和
2.跨域的解決方案 jsonp利用script標(biāo)簽不受跨域限制而形成的一種方案。
// index.html function jsonp({url, param, cb}){ return new Promise((resolve, reject)=>{ let script = document.createElement("script") window[cb] = function(data){ resolve(data); document.body.removeChild(script) } params = {...params, cb} let arrs = []; for(let key in params){ arrs.push(`${key}=${params[key]}`) } script.src = `${url}?${arrs.join("&")}` document.body.appendChild(script) }) } jsonp({ url: "http://localhost:3000/say", params: {wd: "haoxl"}, cb: "show" }).then(data=>{ console.log(data) })
//server.js let express = require("express") let app = express() app.get("/say", function(req, res){ let {wd,cb} = req.query console.log(wd) res.end(`${cb}("hello")`) }) app.listen(3000)
缺點(diǎn):只支持get請(qǐng)求,不支持post、put、delete等;不安全,容易受xss攻擊。cors
跨域資源共享標(biāo)準(zhǔn)新增了一組 HTTP 首部字段,允許服務(wù)器聲明哪些源站有權(quán)限訪問哪些資源。另外,規(guī)范要求,對(duì)那些可能對(duì)服務(wù)器數(shù)據(jù)產(chǎn)生副作用的 HTTP 請(qǐng)求方法(特別是 GET 以外的 HTTP 請(qǐng)求,或者搭配某些 MIME 類型的 POST 請(qǐng)求),瀏覽器必須首先使用 OPTIONS 方法發(fā)起一個(gè)預(yù)檢請(qǐng)求(preflight request),從而獲知服務(wù)端是否允許該跨域請(qǐng)求。服務(wù)器確認(rèn)允許之后,才發(fā)起實(shí)際的 HTTP 請(qǐng)求。在預(yù)檢請(qǐng)求的返回中,服務(wù)器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認(rèn)證相關(guān)數(shù)據(jù))。
Nice to meet you
// server1.js let express = require("express"); let app = express(); app.use(express.static(__dirname)); app.listen(3000)
// server2.js let express = require("express"); let app = express(); let whiteList = ["http://localhost:3000"] app.use(function(req, res, next){ let origin = req.headers.origin; if(whiteList.includes(origin)){ //設(shè)置那個(gè)源可以訪問我,參數(shù)為 * 時(shí),允許任何人訪問,但是不可以和 cookie 憑證的響應(yīng)頭共同使用 res.setHeader("Access-Control-Allow-Origin", origin); //允許帶有name的請(qǐng)求頭的可以訪問 res.setHeader("Access-Control-Allow-Headers","name"); // 設(shè)置哪些請(qǐng)求方法可訪問 res.setHeader("Access-Control-Allow-Methods", "PUT"); // 設(shè)置帶cookie請(qǐng)求時(shí)允許訪問 res.setHeader("Access-Control-Allow-Credentials", true); // 后臺(tái)改了前端傳的name請(qǐng)示頭后,再傳回去時(shí)瀏覽器會(huì)認(rèn)為不安全,所以要設(shè)置下面這個(gè) res.setHeader("Access-Control-Expose-Headers","name"); // 預(yù)檢的存活時(shí)間-options請(qǐng)示 res.setHeader("Access-Control-Max-Age",3) // 設(shè)置當(dāng)預(yù)請(qǐng)求發(fā)來請(qǐng)求時(shí),不做任何處理 if(req.method === "OPTIONS"){ res.end();//OPTIONS請(qǐng)示不做任何處理 } } next(); }); app.put("/getData", function(req, res){ console.log(req.headers) res.setHeader("name","hello"); res.end("hello world"); } app.get("/getData", function(){ res.end("Nice to meet you") }) app.use(express.static(__dirname)); app.listen(3000)postMessage
對(duì)于兩個(gè)不同頁面的腳本,只有當(dāng)執(zhí)行它們的頁面位于具有相同的協(xié)議(通常為https),端口號(hào)(443為https的默認(rèn)值),以及主機(jī) (兩個(gè)頁面的模數(shù) Document.domain設(shè)置為相同的值) 時(shí),這兩個(gè)腳本才能相互通信。window.postMessage() 方法提供了一種受控機(jī)制來規(guī)避此限制,只要正確的使用,這種方法就很安全。window.postMessage() 方法被調(diào)用時(shí),會(huì)在所有頁面腳本執(zhí)行完畢之后(e.g., 在該方法之后設(shè)置的事件、之前設(shè)置的timeout 事件,etc.)向目標(biāo)窗口派發(fā)一個(gè)MessageEvent消息。 該MessageEvent消息有四個(gè)屬性需要注意:
message 屬性表示該message 的類型;
data 屬性為 window.postMessage 的第一個(gè)參數(shù);
origin 屬性表示調(diào)用window.postMessage() 方法時(shí)調(diào)用頁面的當(dāng)前狀態(tài);
source 屬性記錄調(diào)用 window.postMessage() 方法的窗口信息;
targetOrigin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個(gè)URI。
案例:a.html 給b.html發(fā)消息
// a.html
// b.htmlwindow.name
頁面可能會(huì)因某些限制而改變他的源。腳本可以將 document.domain 的值設(shè)置為其當(dāng)前域或其當(dāng)前域的超級(jí)域。如果將其設(shè)置為其當(dāng)前域的超級(jí)域,則較短的域?qū)⒂糜诤罄m(xù)源檢查。a和b是同域的http://localhost:3000, c是獨(dú)立的http://localhost:4000。
a通過iframe引入c,c把值放到window.name,再把它的src指向和a同域的b,然后在iframe所在的窗口中即可取出name的值。
// a.html
// c.html
//server.js let express = require("express") let app = express(); app.use(express.static(__dirname)); app.listen(4000);location.hash
window.location 只讀屬性,返回一個(gè)Location對(duì)象,其中包含有關(guān)文檔當(dāng)前位置的信息。window.location : 所有字母必須小寫!只要賦給 location 對(duì)象一個(gè)新值,文檔就會(huì)使用新的 URL 加載,就好像使用修改后的 URL 調(diào)用了window.location.assign() 一樣。需要注意的是,安全設(shè)置,如 CORS(跨域資源共享),可能會(huì)限制實(shí)際加載新頁面。案例:a、b同域,c多帶帶一個(gè)域。a現(xiàn)在想訪問c:a通過iframe給c傳一個(gè)hash值,c收到hash值后再創(chuàng)建一個(gè)iframe把值通過hash傳遞給b,b將hash結(jié)果放到a的hash值中。
// a.html
// c.html //接收a傳來的hash值 console.log(location.hash) //創(chuàng)建一個(gè)iframe,把回復(fù)的消息傳給b let iframe = document.createElement("iframe"); iframe.src="http://localhost:3000/b.html#idontloveyou"; document.body.appendChild(iframe);
//b.htmlwindow.domain
window.domain:獲取/設(shè)置當(dāng)前文檔的原始域部分。
案例:解決一級(jí)域與二級(jí)域之間通信。 模擬時(shí)需要?jiǎng)?chuàng)建兩個(gè)不同域的域名用來測(cè)試,打開C:WindowsSystem32driversetc 該路徑下找到 hosts 文件,在最下面創(chuàng)建一個(gè)一級(jí)域名和一個(gè)二級(jí)域名。改為:
127.0.0.1 www.haoxl.com 127.0.0.1 test.haoxl.com
預(yù)設(shè)a.html = http://www.haoxl.com,
b.html = http://test.haoxl.com
// a.html
// b.html document.domain = "haoxl.com" var a = "hello world"websocket
WebSocket對(duì)象提供了用于創(chuàng)建和管理 WebSocket 連接,以及可以通過該連接發(fā)送和接收數(shù)據(jù)的 API。它是基于TCP的全雙工通信,即服務(wù)端和客戶端可以雙向進(jìn)行通訊,并且允許跨域通訊。基本協(xié)議有ws://(非加密)和wss://(加密)
//socket.html let socket = new WebSocket("ws://localhost:3000"); // 給服務(wù)器發(fā)消息 socket.onopen = function() { socket.send("hello server") } // 接收服務(wù)器回復(fù)的消息 socket.onmessage = function(e) { console.log(e.data) } // server.js let express = require("express"); let app = express(); let WebSocket = require("ws");//npm i ws // 設(shè)置服務(wù)器域?yàn)?000端口 let wss = new WebSocket.Server({port:3000}); //連接 wss.on("connection", function(ws){ // 接收客戶端傳來的消息 ws.on("message", function(data){ console.log(data); // 服務(wù)端回復(fù)消息 ws.send("hello client") }) })Nginx
Nginx (engine x) 是一個(gè)高性能的HTTP和反向代理服務(wù)器,也是一個(gè)IMAP/POP3/SMTP服務(wù)器。案例:在nginx根目錄下創(chuàng)建json/a.json,里面隨便放些內(nèi)容
// client.html let xhr = new XMLHttpRequest; xhr.open("get", "http://localhost/a.json", true); xhr.onreadystatechange = function() { if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304 ){ console.log(xhr.response); } } }
// server.js let express = require("express"); let app = express(); app.use(express.static(__dirname)); app.listen(3000);
// nginx.conf location / {// 代表輸入/時(shí)默認(rèn)去打開root目錄下的html文件夾 root html; index index.html index.htm; } location ~.*.json{//代表輸入任意.json后去打開json文件夾 root json; add_header "Access-Control-Allow-Origin" "*"; }http-proxy-middleware
NodeJS 中間件 http-proxy-middleware 實(shí)現(xiàn)跨域代理,原理大致與 nginx 相同,都是通過啟一個(gè)代理服務(wù)器,實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)發(fā),也可以通過設(shè)置 cookieDomainRewrite 參數(shù)修改響應(yīng)頭中 cookie 中的域名,實(shí)現(xiàn)當(dāng)前域的 cookie 寫入,方便接口登錄認(rèn)證。
vue框架:利用 node + webpack + webpack-dev-server 代理接口跨域。在開發(fā)環(huán)境下,由于 Vue 渲染服務(wù)和接口代理服務(wù)都是 webpack-dev-server,所以頁面與代理接口之間不再跨域,無須設(shè)置 Headers 跨域信息了。
module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: "/login", target: "http://www.proxy2.com:8080", // 代理跨域目標(biāo)接口 changeOrigin: true, secure: false, // 當(dāng)代理某些 https 服務(wù)報(bào)錯(cuò)時(shí)用 cookieDomainRewrite: "www.domain1.com" // 可以為 false,表示不修改 }], noInfo: true } }
非vue框架的跨域(2 次跨域)
nginx跨域
// 中間代理服務(wù)器 var express = require("express"); var proxy = require("http-proxy-middleware"); var app = express(); app.use( "/", proxy({ // 代理跨域目標(biāo)接口 target: "http://www.proxy2.com:8080", changeOrigin: true, // 修改響應(yīng)頭信息,實(shí)現(xiàn)跨域并允許帶 cookie onProxyRes: function(proxyRes, req, res) { res.header("Access-Control-Allow-Origin", "http://www.proxy1.com"); res.header("Access-Control-Allow-Credentials", "true"); }, // 修改響應(yīng)信息中的 cookie 域名 cookieDomainRewrite: "www.proxy1.com" // 可以為 false,表示不修改 }) ); app.listen(3000);
// 服務(wù)器 var http = require("http"); var server = http.createServer(); var qs = require("querystring"); server.on("request", function(req, res) { var params = qs.parse(req.url.substring(2)); // 向前臺(tái)寫 cookie res.writeHead(200, { "Set-Cookie": "l=a123456;Path=/;Domain=www.proxy2.com;HttpOnly" // HttpOnly:腳本無法讀取 }); res.write(JSON.stringify(params)); res.end(); }); server.listen("8080");
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/96411.html
摘要:常用跨域方法總結(jié)為什么要跨域因?yàn)闉g覽器的一種安全機(jī)制同源策略的限制,導(dǎo)致不能直接獲取不同源的資源,所以要跨域。那么什么才叫同源呢協(xié)議相同域名相同端口號(hào)相同圖來自參見最后下面介紹常用的幾種跨域方法。 常用跨域方法總結(jié) 為什么要跨域? 因?yàn)闉g覽器的一種安全機(jī)制——同源策略的限制,導(dǎo)致不能直接獲取不同源的資源,所以要跨域。 同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行...
摘要:常用跨域方法總結(jié)為什么要跨域因?yàn)闉g覽器的一種安全機(jī)制同源策略的限制,導(dǎo)致不能直接獲取不同源的資源,所以要跨域。那么什么才叫同源呢協(xié)議相同域名相同端口號(hào)相同圖來自參見最后下面介紹常用的幾種跨域方法。 常用跨域方法總結(jié) 為什么要跨域? 因?yàn)闉g覽器的一種安全機(jī)制——同源策略的限制,導(dǎo)致不能直接獲取不同源的資源,所以要跨域。 同源策略限制了從同一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行...
摘要:關(guān)于項(xiàng)目的基本描述,參見開發(fā)微信商城項(xiàng)目總結(jié)之一項(xiàng)目介紹開發(fā)微信商城項(xiàng)目總結(jié)之二配置開發(fā)微信商城項(xiàng)目總結(jié)之三根據(jù)不同的開發(fā)環(huán)境做配置之前處理跨域問題是通過,但是只有開發(fā)環(huán)境是跨域的,代碼打包后上傳到服務(wù)器便不再跨域,所以在本地做了判斷,判斷 關(guān)于項(xiàng)目的基本描述,參見 vue開發(fā)微信商城項(xiàng)目總結(jié)之一–項(xiàng)目介紹 vue開發(fā)微信商城項(xiàng)目總結(jié)之二–Eslint配置 vue開發(fā)微信商城項(xiàng)目總結(jié)之...
摘要:常用跨域方法總結(jié)上篇文章介紹了幾種常用的跨域方法常用跨域方法總結(jié),本片為上一篇的補(bǔ)充,對(duì)比較重要的詳細(xì)介紹。出于安全原因,從腳本內(nèi)發(fā)起的跨源請(qǐng)求會(huì)受到一定限制。當(dāng)開發(fā)者使用對(duì)象發(fā)起跨域請(qǐng)求時(shí),它們已經(jīng)被設(shè)置就緒。 常用跨域方法總結(jié)(2)——CORS 上篇文章介紹了幾種常用的跨域方法:常用跨域方法總結(jié),本片為上一篇的補(bǔ)充,對(duì)比較重要的Cross Origin Resource Shari...
閱讀 632·2021-08-17 10:15
閱讀 1715·2021-07-30 14:57
閱讀 1971·2019-08-30 15:55
閱讀 2813·2019-08-30 15:55
閱讀 2704·2019-08-30 15:44
閱讀 662·2019-08-30 14:13
閱讀 2380·2019-08-30 13:55
閱讀 2588·2019-08-26 13:56