摘要:跨域的解決方案利用標簽不受跨域限制而形成的一種方案。跨域資源共享標準新增了一組首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。它是基于的全雙工通信即服務端和客戶端可以雙向進行通訊,并且允許跨域通訊。
1.什么是跨域
同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的重要安全機制。同源指:協議、域名、端口號必須一致。
同源策略控制了不同源之間的交互,例如在使用XMLHttpRequest 或 標簽時則會受到同源策略的約束。這些交互通常分為三類:
通常允許跨域寫操作(Cross-origin writes)。例如鏈接(links),重定向以及表單提交。特定少數的HTTP請求需要添加 preflight。
通常允許跨域資源嵌入(Cross-origin embedding)。
通常不允許跨域讀操作(Cross-origin reads)。但常可以通過內嵌資源來巧妙的進行讀取訪問。例如可以讀取嵌入圖片的高度和寬度,調用內嵌腳本的方法,或availability of an embedded resource.
下面為允許跨域資源嵌入的示例,即一些不受同源策略影響的標簽示例:
標簽嵌入跨域腳本。語法錯誤信息只能在同源腳本中捕捉到。
標簽嵌入CSS。由于CSS的松散的語法規則,CSS的跨域需要一個設置正確的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標簽不受跨域限制而形成的一種方案。
// 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)
缺點:只支持get請求,不支持post、put、delete等;不安全,容易受xss攻擊。cors
跨域資源共享標準新增了一組 HTTP 首部字段,允許服務器聲明哪些源站有權限訪問哪些資源。另外,規范要求,對那些可能對服務器數據產生副作用的 HTTP 請求方法(特別是 GET 以外的 HTTP 請求,或者搭配某些 MIME 類型的 POST 請求),瀏覽器必須首先使用 OPTIONS 方法發起一個預檢請求(preflight request),從而獲知服務端是否允許該跨域請求。服務器確認允許之后,才發起實際的 HTTP 請求。在預檢請求的返回中,服務器端也可以通知客戶端,是否需要攜帶身份憑證(包括 Cookies 和 HTTP 認證相關數據)。
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)){ //設置那個源可以訪問我,參數為 * 時,允許任何人訪問,但是不可以和 cookie 憑證的響應頭共同使用 res.setHeader("Access-Control-Allow-Origin", origin); //允許帶有name的請求頭的可以訪問 res.setHeader("Access-Control-Allow-Headers","name"); // 設置哪些請求方法可訪問 res.setHeader("Access-Control-Allow-Methods", "PUT"); // 設置帶cookie請求時允許訪問 res.setHeader("Access-Control-Allow-Credentials", true); // 后臺改了前端傳的name請示頭后,再傳回去時瀏覽器會認為不安全,所以要設置下面這個 res.setHeader("Access-Control-Expose-Headers","name"); // 預檢的存活時間-options請示 res.setHeader("Access-Control-Max-Age",3) // 設置當預請求發來請求時,不做任何處理 if(req.method === "OPTIONS"){ res.end();//OPTIONS請示不做任何處理 } } 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
對于兩個不同頁面的腳本,只有當執行它們的頁面位于具有相同的協議(通常為https),端口號(443為https的默認值),以及主機 (兩個頁面的模數 Document.domain設置為相同的值) 時,這兩個腳本才能相互通信。window.postMessage() 方法提供了一種受控機制來規避此限制,只要正確的使用,這種方法就很安全。window.postMessage() 方法被調用時,會在所有頁面腳本執行完畢之后(e.g., 在該方法之后設置的事件、之前設置的timeout 事件,etc.)向目標窗口派發一個MessageEvent消息。 該MessageEvent消息有四個屬性需要注意:
message 屬性表示該message 的類型;
data 屬性為 window.postMessage 的第一個參數;
origin 屬性表示調用window.postMessage() 方法時調用頁面的當前狀態;
source 屬性記錄調用 window.postMessage() 方法的窗口信息;
targetOrigin屬性來指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示無限制)或者一個URI。
案例:a.html 給b.html發消息
// a.html
// b.htmlwindow.name
頁面可能會因某些限制而改變他的源。腳本可以將 document.domain 的值設置為其當前域或其當前域的超級域。如果將其設置為其當前域的超級域,則較短的域將用于后續源檢查。a和b是同域的http://localhost:3000, c是獨立的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 只讀屬性,返回一個Location對象,其中包含有關文檔當前位置的信息。window.location : 所有字母必須小寫!只要賦給 location 對象一個新值,文檔就會使用新的 URL 加載,就好像使用修改后的 URL 調用了window.location.assign() 一樣。需要注意的是,安全設置,如 CORS(跨域資源共享),可能會限制實際加載新頁面。案例:a、b同域,c多帶帶一個域。a現在想訪問c:a通過iframe給c傳一個hash值,c收到hash值后再創建一個iframe把值通過hash傳遞給b,b將hash結果放到a的hash值中。
// a.html
// c.html //接收a傳來的hash值 console.log(location.hash) //創建一個iframe,把回復的消息傳給b let iframe = document.createElement("iframe"); iframe.src="http://localhost:3000/b.html#idontloveyou"; document.body.appendChild(iframe);
//b.htmlwindow.domain
window.domain:獲取/設置當前文檔的原始域部分。
案例:解決一級域與二級域之間通信。 模擬時需要創建兩個不同域的域名用來測試,打開C:WindowsSystem32driversetc 該路徑下找到 hosts 文件,在最下面創建一個一級域名和一個二級域名。改為:
127.0.0.1 www.haoxl.com 127.0.0.1 test.haoxl.com
預設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對象提供了用于創建和管理 WebSocket 連接,以及可以通過該連接發送和接收數據的 API。它是基于TCP的全雙工通信,即服務端和客戶端可以雙向進行通訊,并且允許跨域通訊。基本協議有ws://(非加密)和wss://(加密)
//socket.html let socket = new WebSocket("ws://localhost:3000"); // 給服務器發消息 socket.onopen = function() { socket.send("hello server") } // 接收服務器回復的消息 socket.onmessage = function(e) { console.log(e.data) } // server.js let express = require("express"); let app = express(); let WebSocket = require("ws");//npm i ws // 設置服務器域為3000端口 let wss = new WebSocket.Server({port:3000}); //連接 wss.on("connection", function(ws){ // 接收客戶端傳來的消息 ws.on("message", function(data){ console.log(data); // 服務端回復消息 ws.send("hello client") }) })Nginx
Nginx (engine x) 是一個高性能的HTTP和反向代理服務器,也是一個IMAP/POP3/SMTP服務器。案例:在nginx根目錄下創建json/a.json,里面隨便放些內容
// 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 / {// 代表輸入/時默認去打開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 實現跨域代理,原理大致與 nginx 相同,都是通過啟一個代理服務器,實現數據的轉發,也可以通過設置 cookieDomainRewrite 參數修改響應頭中 cookie 中的域名,實現當前域的 cookie 寫入,方便接口登錄認證。
vue框架:利用 node + webpack + webpack-dev-server 代理接口跨域。在開發環境下,由于 Vue 渲染服務和接口代理服務都是 webpack-dev-server,所以頁面與代理接口之間不再跨域,無須設置 Headers 跨域信息了。
module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: "/login", target: "http://www.proxy2.com:8080", // 代理跨域目標接口 changeOrigin: true, secure: false, // 當代理某些 https 服務報錯時用 cookieDomainRewrite: "www.domain1.com" // 可以為 false,表示不修改 }], noInfo: true } }
非vue框架的跨域(2 次跨域)
nginx跨域
// 中間代理服務器 var express = require("express"); var proxy = require("http-proxy-middleware"); var app = express(); app.use( "/", proxy({ // 代理跨域目標接口 target: "http://www.proxy2.com:8080", changeOrigin: true, // 修改響應頭信息,實現跨域并允許帶 cookie onProxyRes: function(proxyRes, req, res) { res.header("Access-Control-Allow-Origin", "http://www.proxy1.com"); res.header("Access-Control-Allow-Credentials", "true"); }, // 修改響應信息中的 cookie 域名 cookieDomainRewrite: "www.proxy1.com" // 可以為 false,表示不修改 }) ); app.listen(3000);
// 服務器 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)); // 向前臺寫 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");
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96411.html
摘要:常用跨域方法總結為什么要跨域因為瀏覽器的一種安全機制同源策略的限制,導致不能直接獲取不同源的資源,所以要跨域。那么什么才叫同源呢協議相同域名相同端口號相同圖來自參見最后下面介紹常用的幾種跨域方法。 常用跨域方法總結 為什么要跨域? 因為瀏覽器的一種安全機制——同源策略的限制,導致不能直接獲取不同源的資源,所以要跨域。 同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行...
摘要:關于項目的基本描述,參見開發微信商城項目總結之一項目介紹開發微信商城項目總結之二配置開發微信商城項目總結之三根據不同的開發環境做配置之前處理跨域問題是通過,但是只有開發環境是跨域的,代碼打包后上傳到服務器便不再跨域,所以在本地做了判斷,判斷 關于項目的基本描述,參見 vue開發微信商城項目總結之一–項目介紹 vue開發微信商城項目總結之二–Eslint配置 vue開發微信商城項目總結之...
摘要:常用跨域方法總結上篇文章介紹了幾種常用的跨域方法常用跨域方法總結,本片為上一篇的補充,對比較重要的詳細介紹。出于安全原因,從腳本內發起的跨源請求會受到一定限制。當開發者使用對象發起跨域請求時,它們已經被設置就緒。 常用跨域方法總結(2)——CORS 上篇文章介紹了幾種常用的跨域方法:常用跨域方法總結,本片為上一篇的補充,對比較重要的Cross Origin Resource Shari...
閱讀 640·2021-08-17 10:15
閱讀 1724·2021-07-30 14:57
閱讀 1978·2019-08-30 15:55
閱讀 2820·2019-08-30 15:55
閱讀 2708·2019-08-30 15:44
閱讀 670·2019-08-30 14:13
閱讀 2386·2019-08-30 13:55
閱讀 2592·2019-08-26 13:56