最近這段時(shí)間總有小伙伴問小編2022年02月26日整理發(fā)布:前端跨域解決方案有哪些是什么,小編為此在網(wǎng)上搜尋了一些有關(guān)于2022年02月26日整理發(fā)布:前端跨域解決方案有哪些的知識(shí)送給大家,希望能解答各位小伙伴的疑惑。
我給大家講一下前端跨域解決方案。相信朋友們也應(yīng)該非常關(guān)注這個(gè)話題?,F(xiàn)在給朋友們說說前端跨域解決方案。邊肖還收集了一些關(guān)于前端跨領(lǐng)域解決方案的相關(guān)信息。我希望你看到它們時(shí)會(huì)喜歡。
(資料圖片)
方案:1。jsonp;2.“document.domain”和iframe;3.“l(fā)ocation.hash”和iframe;4.“window.name”和iframe;5、短信;6、CORS;7.nginx代理等等。
本教程運(yùn)行環(huán)境:windows7系統(tǒng),nodejs 14.15.4,Dell G3電腦。什么是跨域?跨域是指一個(gè)域中的文檔或腳本試圖請(qǐng)求另一個(gè)域中的資源,這里泛指跨域。
廣義跨域:
1.)資源跳轉(zhuǎn):一個(gè)鏈接,重定向和表單提交。
2.)資源嵌入:鏈接、腳本、img、框架等dom標(biāo)簽。以及樣式中的background:url()和@font-face()等文件鏈。
3)腳本請(qǐng)求:js發(fā)起的ajax請(qǐng)求,dom和js對(duì)象的跨域操作等。其實(shí)我們通常所說的跨域是一種受瀏覽器同源策略限制的請(qǐng)求場(chǎng)景。
什么是同源策略?
同源策略/SOP(同源策略)是一種約定,由Netscape于1995年引入瀏覽器。是瀏覽器最核心、最基礎(chǔ)的安全功能。如果沒有同源策略,瀏覽器很容易受到XSS、CSFR和其他攻擊。同源是指‘協(xié)議域名端口’相同,即使兩個(gè)不同的域名指向同一個(gè)ip地址,也不是同源。
相應(yīng)的策略限制以下行為:
1.)無(wú)法讀取cookies、LocalStorage和IndexDB。
2.)無(wú)法獲取DOM和Js對(duì)象。
3.)AJAX請(qǐng)求不能發(fā)送常見跨域場(chǎng)景的URL來表示是否允許通信。
http://www.domain.com/a.js
http://www.domain.com/b.js相同的域名,不同的文件或路徑是允許的。
http://www.domain.com/lab/c.js
http://www . domain.com :8000/a . js
http://www.domain.com/b.js相同的域名,不同的端口是不允許的。
http://www.domain.com/a.js
在https://www.domain.com/b.js,不同的協(xié)議不允許使用相同的域名。
http://www.domain.com/a.js
http://192.168.4.12/b.js域名和對(duì)應(yīng)同一ip的域名不允許。
http://www.domain.com/a.js
http://x.domain.com/b.js有相同的主域名,但不同的子域是不允許的。
http://domain.com/c.js
http://www.domain1.com/a.js
http://www.domain2.com/b.js不同域名不允許跨域解決方案1、通過jsonp跨域2、通過document.domain iframe跨域3、location.hash iframe4、通過window.name iframe跨域5、通過postMessage跨域6、跨域資源共享(CORS)7、nginx代理跨域8、nodejs中間件代理跨域9、WebSocket協(xié)議跨域。
1.通過jsonp跨域通常情況下,為了減輕web服務(wù)器上的負(fù)載,我們將js、css、img等靜態(tài)資源分離到另一個(gè)有獨(dú)立域名的服務(wù)器上,然后通過html頁(yè)面中相應(yīng)的標(biāo)簽加載不同域名的靜態(tài)資源,這是瀏覽器允許的。基于這個(gè)原理,我們可以動(dòng)態(tài)創(chuàng)建腳本,然后用參數(shù)請(qǐng)求一個(gè)網(wǎng)址,實(shí)現(xiàn)跨域通信。
1)本地實(shí)現(xiàn):
腳本
var script=document . createelement(" script ");
script . type=" text/JavaScript ";
//向后端傳遞一個(gè)回調(diào)函數(shù)名,方便后端返回時(shí)執(zhí)行前端定義的回調(diào)函數(shù)。
script . src=" http://www . domain 2.com :8080/log in?user=admin callback=handle callback ";
document.head.appendChild(腳本);
//回調(diào)執(zhí)行函數(shù)
功能
n handleCallback(res) { alert(JSON.stringify(res)); } </script>服務(wù)端返回如下(返回時(shí)即執(zhí)行全局函數(shù)):
handleCallback({"status": true, "user": "admin"})2.)jquery ajax:
$.ajax({ url: "http://www.domain2.com:8080/login", type: "get", dataType: "jsonp", // 請(qǐng)求方式為jsonp jsonpCallback: "handleCallback", // 自定義回調(diào)函數(shù)名 data: {}});3.)vue.js:
this.$http.jsonp("http://www.domain2.com:8080/login", { params: {}, jsonp: "handleCallback"}).then((res) => { console.log(res); })后端node.js代碼示例:
var querystring = require("querystring");var http = require("http");var server = http.createServer();server.on("request", function(req, res) { var params = qs.parse(req.url.split("?")[1]); var fn = params.callback; // jsonp返回設(shè)置 res.writeHead(200, { "Content-Type": "text/javascript" }); res.write(fn + "(" + JSON.stringify(params) + ")"); res.end();});server.listen("8080");console.log("Server is running at port 8080...");jsonp缺點(diǎn):只能實(shí)現(xiàn)get一種請(qǐng)求。
二、 document.domain + iframe跨域此方案僅限主域相同,子域不同的跨域應(yīng)用場(chǎng)景。
實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域。
1.)父窗口:(http://www.domain.com/a.html)
<script> document.domain = "domain.com"; var user = "admin";</script>2.)子窗口:(http://child.domain.com/b.html)
<script> document.domain = "domain.com"; // 獲取父窗口中變量 alert("get js data from parent ---> " + window.parent.user);</script>三、 location.hash + iframe跨域實(shí)現(xiàn)原理: a欲與b跨域相互通信,通過中間頁(yè)c來實(shí)現(xiàn)。 三個(gè)頁(yè)面,不同域之間利用iframe的location.hash傳值,相同域之間直接js訪問來通信。
具體實(shí)現(xiàn):A域:a.html -> B域:b.html -> A域:c.html,a與b不同域只能通過hash值單向通信,b與c也不同域也只能單向通信,但c與a同域,所以c可通過parent.parent訪問a頁(yè)面所有對(duì)象。
1.)a.html:(http://www.domain1.com/a.html)
<script> var iframe = document.getElementById("iframe"); // 向b.html傳hash值 setTimeout(function() { iframe.src = iframe.src + "#user=admin"; }, 1000); // 開放給同域c.html的回調(diào)方法 function onCallback(res) { alert("data from c.html ---> " + res); }</script>2.)b.html:(http://www.domain2.com/b.html)
<script> // 監(jiān)聽b.html傳來的hash值 window.onhashchange = function () { // 再通過操作同域a.html的js回調(diào),將結(jié)果傳回 window.parent.parent.onCallback("hello: " + location.hash.replace("#user=", "")); };</script>3.)c.html:(http://www.domain1.com/c.html)
<script> // 監(jiān)聽b.html傳來的hash值 window.onhashchange = function () { // 再通過操作同域a.html的js回調(diào),將結(jié)果傳回 window.parent.parent.onCallback("hello: " + location.hash.replace("#user=", "")); };</script>四、 window.name + iframe跨域window.name屬性的獨(dú)特之處:name值在不同的頁(yè)面(甚至不同域名)加載后依舊存在,并且可以支持非常長(zhǎng)的 name 值(2MB)。
1.)a.html:(http://www.domain1.com/a.html)
var proxy = function(url, callback) { var state = 0; var iframe = document.createElement("iframe"); // 加載跨域頁(yè)面 iframe.src = url; // onload事件會(huì)觸發(fā)2次,第1次加載跨域頁(yè),并留存數(shù)據(jù)于window.name iframe.onload = function() { if (state === 1) { // 第2次onload(同域proxy頁(yè))成功后,讀取同域window.name中數(shù)據(jù) callback(iframe.contentWindow.name); destoryFrame(); } else if (state === 0) { // 第1次onload(跨域頁(yè))成功后,切換到同域代理頁(yè)面 iframe.contentWindow.location = "http://www.domain1.com/proxy.html"; state = 1; } }; document.body.appendChild(iframe); // 獲取數(shù)據(jù)以后銷毀這個(gè)iframe,釋放內(nèi)存;這也保證了安全(不被其他域frame js訪問) function destoryFrame() { iframe.contentWindow.document.write(""); iframe.contentWindow.close(); document.body.removeChild(iframe); }};// 請(qǐng)求跨域b頁(yè)面數(shù)據(jù)proxy("http://www.domain2.com/b.html", function(data){ alert(data);});2.)proxy.html:(http://www.domain1.com/proxy....中間代理頁(yè),與a.html同域,內(nèi)容為空即可。
3.)b.html:(http://www.domain2.com/b.html)
<script> window.name = "This is domain2 data!";</script>總結(jié):通過iframe的src屬性由外域轉(zhuǎn)向本地域,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域。這個(gè)就巧妙地繞過了瀏覽器的跨域訪問限制,但同時(shí)它又是安全操作。
五、 postMessage跨域postMessage是HTML5 XMLHttpRequest Level 2中的API,且是為數(shù)不多可以跨域操作的window屬性之一,它可用于解決以下方面的問題:a.) 頁(yè)面和其打開的新窗口的數(shù)據(jù)傳遞b.) 多窗口之間消息傳遞c.) 頁(yè)面與嵌套的iframe消息傳遞d.) 上面三個(gè)場(chǎng)景的跨域數(shù)據(jù)傳遞
用法:postMessage(data,origin)方法接受兩個(gè)參數(shù)data: html5規(guī)范支持任意基本類型或可復(fù)制的對(duì)象,但部分瀏覽器只支持字符串,所以傳參時(shí)最好用JSON.stringify()序列化。origin: 協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*",表示可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話設(shè)置為"/"。
1.)a.html:(http://www.domain1.com/a.html)
<script> var iframe = document.getElementById("iframe"); iframe.onload = function() { var data = { name: "aym" }; // 向domain2傳送跨域數(shù)據(jù) iframe.contentWindow.postMessage(JSON.stringify(data), "http://www.domain2.com"); }; // 接受domain2返回?cái)?shù)據(jù) window.addEventListener("message", function(e) { alert("data from domain2 ---> " + e.data); }, false);</script>2.)b.html:(http://www.domain2.com/b.html)
<script> // 接收domain1的數(shù)據(jù) window.addEventListener("message", function(e) { alert("data from domain1 ---> " + e.data); var data = JSON.parse(e.data); if (data) { data.number = 16; // 處理后再發(fā)回domain1 window.parent.postMessage(JSON.stringify(data), "http://www.domain1.com"); } }, false);</script>六、 跨域資源共享(CORS)普通跨域請(qǐng)求:只服務(wù)端設(shè)置Access-Control-Allow-Origin即可,前端無(wú)須設(shè)置,若要帶cookie請(qǐng)求:前后端都需要設(shè)置。
需注意的是:由于同源策略的限制,所讀取的cookie為跨域請(qǐng)求接口所在域的cookie,而非當(dāng)前頁(yè)。如果想實(shí)現(xiàn)當(dāng)前頁(yè)cookie的寫入,可參考下文:七、nginx反向代理中設(shè)置proxy_cookie_domain 和 八、NodeJs中間件代理中cookieDomainRewrite參數(shù)的設(shè)置。
目前,所有瀏覽器都支持該功能(IE8+:IE8/9需要使用XDomainRequest對(duì)象來支持CORS)),CORS也已經(jīng)成為主流的跨域解決方案。
1、 前端設(shè)置:1.)原生ajax
// 前端設(shè)置是否帶cookiexhr.withCredentials = true;示例代碼:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容// 前端設(shè)置是否帶cookiexhr.withCredentials = true;xhr.open("post", "http://www.domain2.com:8080/login", true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");xhr.send("user=admin");xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { alert(xhr.responseText); }};2.)jQuery ajax
$.ajax({ ... xhrFields: { withCredentials: true // 前端設(shè)置是否帶cookie }, crossDomain: true, // 會(huì)讓請(qǐng)求頭中包含跨域的額外信息,但不會(huì)含cookie ...});3.)vue框架
a.) axios設(shè)置:
axios.defaults.withCredentials = trueb.) vue-resource設(shè)置:
Vue.http.options.credentials = true2、 服務(wù)端設(shè)置:若后端設(shè)置成功,前端瀏覽器控制臺(tái)則不會(huì)出現(xiàn)跨域報(bào)錯(cuò)信息,反之,說明沒設(shè)成功。
1.)Java后臺(tái):
/* * 導(dǎo)入包:import javax.servlet.http.HttpServletResponse; * 接口參數(shù)中定義:HttpServletResponse response */// 允許跨域訪問的域名:若有端口需寫全(協(xié)議+域名+端口),若沒有端口末尾不用加"/"response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // 允許前端帶認(rèn)證cookie:?jiǎn)⒂么隧?xiàng)后,上面的域名不能為"*",必須指定具體的域名,否則瀏覽器會(huì)提示response.setHeader("Access-Control-Allow-Credentials", "true"); // 提示OPTIONS預(yù)檢時(shí),后端需要設(shè)置的兩個(gè)常用自定義頭response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");2.)Nodejs后臺(tái)示例:
var http = require("http");var server = http.createServer();var qs = require("querystring");server.on("request", function(req, res) { var postData = ""; // 數(shù)據(jù)塊接收中 req.addListener("data", function(chunk) { postData += chunk; }); // 數(shù)據(jù)接收完畢 req.addListener("end", function() { postData = qs.parse(postData); // 跨域后臺(tái)設(shè)置 res.writeHead(200, { "Access-Control-Allow-Credentials": "true", // 后端允許發(fā)送Cookie "Access-Control-Allow-Origin": "http://www.domain1.com", // 允許訪問的域(協(xié)議+域名+端口) /* * 此處設(shè)置的cookie還是domain2的而非domain1,因?yàn)楹蠖艘膊荒芸缬驅(qū)慶ookie(nginx反向代理可以實(shí)現(xiàn)), * 但只要domain2中寫入一次cookie認(rèn)證,后面的跨域接口都能從domain2中獲取cookie,從而實(shí)現(xiàn)所有的接口都能跨域訪問 */ "Set-Cookie": "l=a123456;Path=/;Domain=www.domain2.com;HttpOnly" // HttpOnly的作用是讓js無(wú)法讀取cookie }); res.write(JSON.stringify(postData)); res.end(); });});server.listen("8080");console.log("Server is running at port 8080...");七、 nginx代理跨域1、 nginx配置解決iconfont跨域瀏覽器跨域訪問js、css、img等常規(guī)靜態(tài)資源被同源策略許可,但iconfont字體文件(eot|otf|ttf|woff|svg)例外,此時(shí)可在nginx的靜態(tài)資源服務(wù)器中加入以下配置。
location / { add_header Access-Control-Allow-Origin *;}2、 nginx反向代理接口跨域跨域原理: 同源策略是瀏覽器的安全策略,不是HTTP協(xié)議的一部分。服務(wù)器端調(diào)用HTTP接口只是使用HTTP協(xié)議,不會(huì)執(zhí)行JS腳本,不需要同源策略,也就不存在跨越問題。
實(shí)現(xiàn)思路:通過nginx配置一個(gè)代理服務(wù)器(域名與domain1相同,端口不同)做跳板機(jī),反向代理訪問domain2接口,并且可以順便修改cookie中domain信息,方便當(dāng)前域cookie寫入,實(shí)現(xiàn)跨域登錄。
nginx具體配置:
#proxy服務(wù)器server { listen 81; server_name www.domain1.com; location / { proxy_pass http://www.domain2.com:8080; #反向代理 proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名 index index.html index.htm; # 當(dāng)用webpack-dev-server等中間件代理接口訪問nignx時(shí),此時(shí)無(wú)瀏覽器參與,故沒有同源限制,下面的跨域配置可不啟用 add_header Access-Control-Allow-Origin http://www.domain1.com; #當(dāng)前端只跨域不帶cookie時(shí),可為* add_header Access-Control-Allow-Credentials true; }}1.) 前端代碼示例:
var xhr = new XMLHttpRequest();// 前端開關(guān):瀏覽器是否讀寫cookiexhr.withCredentials = true;// 訪問nginx中的代理服務(wù)器xhr.open("get", "http://www.domain1.com:81/?user=admin", true);xhr.send();2.) Nodejs后臺(tái)示例:
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.domain2.com;HttpOnly" // HttpOnly:腳本無(wú)法讀取 }); res.write(JSON.stringify(params)); res.end();});server.listen("8080");console.log("Server is running at port 8080...");八、 Nodejs中間件代理跨域node中間件實(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)證。
1、 非vue框架的跨域(2次跨域)利用node + express + http-proxy-middleware搭建一個(gè)proxy服務(wù)器。
1.)前端代碼示例:
var xhr = new XMLHttpRequest();// 前端開關(guān):瀏覽器是否讀寫cookiexhr.withCredentials = true;// 訪問http-proxy-middleware代理服務(wù)器xhr.open("get", "http://www.domain1.com:3000/login?user=admin", true);xhr.send();2.)中間件服務(wù)器:
var express = require("express");var proxy = require("http-proxy-middleware");var app = express();app.use("/", proxy({ // 代理跨域目標(biāo)接口 target: "http://www.domain2.com:8080", changeOrigin: true, // 修改響應(yīng)頭信息,實(shí)現(xiàn)跨域并允許帶cookie onProxyRes: function(proxyRes, req, res) { res.header("Access-Control-Allow-Origin", "http://www.domain1.com"); res.header("Access-Control-Allow-Credentials", "true"); }, // 修改響應(yīng)信息中的cookie域名 cookieDomainRewrite: "www.domain1.com" // 可以為false,表示不修改}));app.listen(3000);console.log("Proxy server is listen at port 3000...");3.)Nodejs后臺(tái)同(六:nginx)
2、 vue框架的跨域(1次跨域)利用node + webpack + webpack-dev-server代理接口跨域。在開發(fā)環(huán)境下,由于vue渲染服務(wù)和接口代理服務(wù)都是webpack-dev-server同一個(gè),所以頁(yè)面與代理接口之間不再跨域,無(wú)須設(shè)置headers跨域信息了。
webpack.config.js部分配置:
module.exports = { entry: {}, module: {}, ... devServer: { historyApiFallback: true, proxy: [{ context: "/login", target: "http://www.domain2.com:8080", // 代理跨域目標(biāo)接口 changeOrigin: true, secure: false, // 當(dāng)代理某些https服務(wù)報(bào)錯(cuò)時(shí)用 cookieDomainRewrite: "www.domain1.com" // 可以為false,表示不修改 }], noInfo: true }}九、 WebSocket協(xié)議跨域WebSocket protocol是HTML5一種新的協(xié)議。它實(shí)現(xiàn)了瀏覽器與服務(wù)器全雙工通信,同時(shí)允許跨域通訊,是server push技術(shù)的一種很好的實(shí)現(xiàn)。原生WebSocket API使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡(jiǎn)單、靈活的接口,也對(duì)不支持webSocket的瀏覽器提供了向下兼容。
1.)前端代碼:
user input:
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script><script>var socket = io("http://www.domain2.com:8080");// 連接成功處理socket.on("connect", function() { // 監(jiān)聽服務(wù)端消息 socket.on("message", function(msg) { console.log("data from server: ---> " + msg); }); // 監(jiān)聽服務(wù)端關(guān)閉 socket.on("disconnect", function() { console.log("Server socket has closed."); });});document.getElementsByTagName("input")[0].onblur = function() { socket.send(this.value);};</script>2.)Nodejs socket后臺(tái):
var http = require("http");var socket = require("socket.io");// 啟http服務(wù)var server = http.createServer(function(req, res) { res.writeHead(200, { "Content-type": "text/html" }); res.end();});server.listen("8080");console.log("Server is running at port 8080...");// 監(jiān)聽socket連接socket.listen(server).on("connection", function(client) { // 接收信息 client.on("message", function(msg) { client.send("hello:" + msg); console.log("data from client: ---> " + msg); }); // 斷開處理 client.on("disconnect", function() { console.log("Client socket has closed."); });});中間件跨域postmessagenginxcors
更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問:編程視頻??!
以上就是前端跨域解決方案有哪些的詳細(xì)內(nèi)容,更多請(qǐng)關(guān)注php中文網(wǎng)其它相關(guān)文章!
來源:php中文網(wǎng)
標(biāo)簽: 解決方案 反向代理 代理服務(wù)器 回調(diào)函數(shù)