跨域几种方式
一、什么是跨域
?
JavaScript出于安全方面的考慮,不允許跨域調(diào)用其他頁面的對象。那什么是跨域呢,簡單地理解就是因?yàn)镴avaScript同源策略的限制, a.com 域名下的js無法操作 b.com或是 c.a.com 域名下的對象。
當(dāng)協(xié)議、子域名、主域名、端口號中任意一個(gè)不相同時(shí),都算作不同域。不同域之間相互請求資源,就算作“跨域”。例如:http://www.abc.com/index.html 請求 http://www.efg.com/service.php。
有一點(diǎn)必須要注意:跨域并不是請求發(fā)不出去,請求能發(fā)出去,服務(wù)端能收到請求并正常返回結(jié)果,只是結(jié)果被瀏覽器攔截了。之所以會跨域,是因?yàn)槭艿搅送床呗缘南拗?#xff0c;同源策略要求源相同才能正常進(jìn)行通信,即協(xié)議、域名、端口號都完全一致。
大家可以參照下圖,有助于深入理解跨域。
特別說明兩點(diǎn):
第一:如果是協(xié)議和端口造成的跨域問題“前臺”是無能為力的。
第二:在跨域問題上,域僅僅是通過“URL的首部”來識別而不會根據(jù)域名對應(yīng)的IP地址是否相同來判斷。“URL的首部”可以理解為“協(xié)議, 域名和端口必須匹配”。
二、什么是同源策略及其限制
同源策略(Same origin policy)是一種約定,它是瀏覽器最核心也最基本的安全功能,如果缺少了同源策略,則瀏覽器的正常功能可能都會受到影響。
可以說Web是構(gòu)建在同源策略基礎(chǔ)之上的,瀏覽器只是針對同源策略的一種實(shí)現(xiàn)。
同源策略,它是由Netscape提出的一個(gè)著名的安全策略。現(xiàn)在所有支持JavaScript 的瀏覽器都會使用這個(gè)策略。所謂同源是指,域名,協(xié)議,端口相同。 當(dāng)一個(gè)瀏覽器的兩個(gè)tab頁中分別打開來 百度和谷歌的頁面當(dāng)瀏覽器的百度tab頁執(zhí)行一個(gè)腳本的時(shí)候會檢查這個(gè)腳本是屬于哪個(gè)頁面的,即檢查是否同源,只有和百度同源的腳本才會被執(zhí)行。 如果非同源,那么在請求數(shù)據(jù)時(shí),瀏覽器會在控制臺中報(bào)一個(gè)異常,提示拒絕訪問。同源策略限制從一個(gè)源加載的文檔或腳本如何與來自另一個(gè)源的資源進(jìn)行交互。這是一個(gè)用于隔離潛在惡意文件的關(guān)鍵的安全機(jī)制。它的存在可以保護(hù)用戶隱私信息,防止身份偽造等(讀取Cookie)。
同源策略限制內(nèi)容有:
-
Cookie、LocalStorage、IndexedDB 等存儲性內(nèi)容
-
DOM 節(jié)點(diǎn)
-
AJAX 請求不能發(fā)送
但是有三個(gè)標(biāo)簽是允許跨域加載資源:
<imgsrc=XXX>
<linkhref=XXX>
<scriptsrc=XXX>
接下來我們討論下有哪些處理跨域的方法。但所有的跨域都必須經(jīng)過信息提供方的允許。如果未經(jīng)允許即可獲取,那是瀏覽器同源策略出現(xiàn)漏洞。
三、處理跨域方法一——JSONP
1.JSONP原理
利用 <script> 元素的這個(gè)開放策略,網(wǎng)頁可以得到從其他來源動態(tài)產(chǎn)生的 JSON 數(shù)據(jù)。JSONP請求一定需要對方的服務(wù)器做支持才可以。
2.JSONP和AJAX對比
JSONP和AJAX相同,都是客戶端向服務(wù)器端發(fā)送請求,從服務(wù)器端獲取數(shù)據(jù)的方式。但AJAX屬于同源策略,JSONP屬于非同源策略(跨域請求)。
3.JSONP優(yōu)缺點(diǎn)
JSONP優(yōu)點(diǎn)是兼容性好,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題。缺點(diǎn)是僅支持get方法具有局限性。
4.JSONP的流程(以第三方API地址為例,不必考慮后臺程序)
-
聲明一個(gè)回調(diào)函數(shù),其函數(shù)名(如fn)當(dāng)做參數(shù)值,要傳遞給跨域請求數(shù)據(jù)的服務(wù)器,函數(shù)形參為要獲取目標(biāo)數(shù)據(jù)(服務(wù)器返回的data)。
-
創(chuàng)建一個(gè)?<script>標(biāo)簽,把那個(gè)跨域的API數(shù)據(jù)接口地址,賦值給script的src,還要在這個(gè)地址中向服務(wù)器傳遞該函數(shù)名(可以通過問號傳參?:?callback=fn)。
-
服務(wù)器接收到請求后,需要進(jìn)行特殊的處理:把傳遞進(jìn)來的函數(shù)名和它需要給你的數(shù)據(jù)拼接成一個(gè)字符串,例如:傳遞進(jìn)去的函數(shù)名是fn,它準(zhǔn)備好的數(shù)據(jù)是?fn([{"name":"jianshu"}])。
-
-
最后服務(wù)器把準(zhǔn)備的數(shù)據(jù)通過HTTP協(xié)議返回給客戶端,客戶端再調(diào)用執(zhí)行之前聲明的回調(diào)函數(shù)(fn),對返回的數(shù)據(jù)進(jìn)行操作。
<script type="text/javascript">
? ?function fn(data) {
? ? ? ?alert(data.msg);
? ?}
</script>
<script type="text/javascript" src="http://crossdomain.com/jsonServerResponse?jsonp=fn"></script>
其中 fn 是客戶端注冊的回調(diào)的函數(shù),目的獲取跨域服務(wù)器上的json數(shù)據(jù)后,對數(shù)據(jù)進(jìn)行在處理。
最后服務(wù)器返回給客戶端數(shù)據(jù)的格式為:
fn({ msg:'this ?is ?json ?data'})
5.jQuery的jsonp形式
JSONP都是GET和異步請求的,不存在其他的請求方式和同步請求,且jQuery默認(rèn)就會給JSONP的請求清除緩存。
$.ajax({
url:"http://crossdomain.com/jsonServerResponse",
dataType:"jsonp",
type:"get",//可以省略
jsonpCallback:"fn",//->自定義傳遞給服務(wù)器的函數(shù)名,而不是使用jQuery自動生成的,可省略
jsonp:"jsonp",//->把傳遞函數(shù)名的那個(gè)形參callback變?yōu)閖sonp,可省略
success:function (data){
console.log(data);}
});
四、處理跨域方法二——CORS
1.CORS原理
整個(gè)CORS通信過程,都是瀏覽器自動完成,不需要用戶參與。對于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣。瀏覽器一旦發(fā)現(xiàn)AJAX請求跨源,就會自動添加一些附加的頭信息,有時(shí)還會多出一次附加的請求,但用戶不會有感覺。因此,實(shí)現(xiàn)CORS通信的關(guān)鍵是服務(wù)器。只要服務(wù)器實(shí)現(xiàn)了CORS接口,就可以跨源通信。
2.CORS優(yōu)缺點(diǎn)
CORS要求瀏覽器(>IE10)和服務(wù)器的同時(shí)支持,是跨域的根本解決方法,由瀏覽器自動完成。
優(yōu)點(diǎn)在于功能更加強(qiáng)大支持各種HTTP Method,缺點(diǎn)是兼容性不如JSONP。
只需要在服務(wù)器端做一些小小的改造即可:
header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Methods:POST,GET");
例如:網(wǎng)站 http://localhost:63342/ 頁面要請求 http://localhost:3000/users/userlist 頁面,userlist頁面返回json字符串格 {name:'Mr.Cao',gender:'male',career:'IT Education'}:
//在服務(wù)器端設(shè)置同源策略地址
router.get("/userlist", function (req, res, next) { ?
? ?var user = {name: 'Mr.Cao', gender: 'male', career: 'IT Education'}; ?
? ?res.writeHeader(200,{"Access-Control-Allow-Origin":'http://localhost:63342'}); ?
? ?res.write(JSON.stringify(user)); ?
? ?res.end(); ?
});
在響應(yīng)頭上添加 Access-Control-Allow-Origin 屬性,指定同源策略的地址。同源策略默認(rèn)地址是網(wǎng)頁的本身。只要瀏覽器檢測到響應(yīng)頭帶上了CORS,并且允許的源包括了本網(wǎng)站,那么就不會攔截請求響應(yīng)。
五、處理跨域方法三——WebSocket
Websocket是HTML5的一個(gè)持久化的協(xié)議,它實(shí)現(xiàn)了瀏覽器與服務(wù)器的全雙工通信,同時(shí)也是跨域的一種解決方案。WebSocket和HTTP都是應(yīng)用層協(xié)議,都基于 TCP 協(xié)議。但是 WebSocket 是一種雙向通信協(xié)議,在建立連接之后,WebSocket 的 server 與 client 都能主動向?qū)Ψ桨l(fā)送或接收數(shù)據(jù)。同時(shí),WebSocket 在建立連接時(shí)需要借助 HTTP 協(xié)議,連接建立好了之后 client 與 server 之間的雙向通信就與 HTTP 無關(guān)了。
原生WebSocket API使用起來不太方便,我們使用Socket.io,它很好地封裝了webSocket接口,提供了更簡單、靈活的接口,也對不支持webSocket的瀏覽器提供了向下兼容。
//前端代碼:
<div>user input:<input type="text"></div>
<script src="./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>
//Nodejs socket后臺:
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.');
? ?});
});
六、處理跨域方法四——postMessage
如果兩個(gè)網(wǎng)頁不同源,就無法拿到對方的DOM。典型的例子是iframe窗口和 window.open方法打開的窗口,它們與父窗口無法通信。HTML5為了解決這個(gè)問題,引入了一個(gè)全新的API:跨文檔通信 API(Cross-document messaging)。這個(gè)API為window對象新增了一個(gè) window.postMessage 方法,允許跨窗口通信,不論這兩個(gè)窗口是否同源。
postMessage方法的第一個(gè)參數(shù)是具體的信息內(nèi)容,第二個(gè)參數(shù)是接收消息的窗口的源(origin),即"協(xié)議 + 域名 + 端口"。也可以設(shè)為 *,表示不限制域名,向所有窗口發(fā)送。
接下來我們看個(gè)例子: http://localhost:63342/index.html頁面向 http://localhost:3000/message.html傳遞“跨域請求信息”
//發(fā)送信息頁面 http://localhost:63342/index.html
<html lang="en"> ?
<head> ?
? ?<meta charset="UTF-8"> ?
? ?<title>跨域請求</title> ?
</head> ?
<body> ?
? ?<iframe src="http://localhost:3000/users/reg" id="frm"></iframe> ?
? ?<input type="button" value="OK" onclick="run()"> ?
</body> ?
</html> ?
<script> ?
? function ?run(){ ?
? ? ? ?var frm=document.getElementById("frm"); ?
? ? ? ?frm.contentWindow.postMessage("跨域請求信息","http://localhost:3000"); ?
? } ?
</script> ?
//接收信息頁面 http://localhost:3000/message.html
window.addEventListener("message",function(e){ ?//通過監(jiān)聽message事件,可以監(jiān)聽對方發(fā)送的消息。
?console.log(e.data); ?
},false); ?
轉(zhuǎn)載于:https://www.cnblogs.com/chaoyuehedy/p/9908055.html
總結(jié)
- 上一篇: windows常见软件库
- 下一篇: HTTPS安全超文本传输协议