自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

理解跨域及常用解決方案

開(kāi)發(fā) 前端
跨域,相信大家無(wú)論是在工作中還是在面試中經(jīng)常遇到這個(gè)問(wèn)題,常常在網(wǎng)上看到別人所整理的一些方法,看似知道是怎么回事,但如果沒(méi)有動(dòng)手實(shí)踐過(guò),總覺(jué)得自己沒(méi)有真正的掌握,在這里,通過(guò)自己認(rèn)真思考整理一些常用的方法。

跨域,相信大家無(wú)論是在工作中還是在面試中經(jīng)常遇到這個(gè)問(wèn)題,常常在網(wǎng)上看到別人所整理的一些方法,看似知道是怎么回事,但如果沒(méi)有動(dòng)手實(shí)踐過(guò),總覺(jué)得自己沒(méi)有真正的掌握,在這里,通過(guò)自己認(rèn)真思考整理一些常用的方法。

跨域的產(chǎn)生

不用多講,作為一名前端開(kāi)發(fā)人員,相信大家都知道跨域是因?yàn)闉g覽器的同源策略所導(dǎo)致的。所謂同源是指"協(xié)議+域名+端口"三者相同,即便兩個(gè)不同的域名指向同一個(gè)ip地址,也非同源。瀏覽器引入同源策略主要是為了防止XSS,CSRF攻擊。

CSRF(Cross-site request forgery),跨站請(qǐng)求偽造,也被稱(chēng)為:one click attack/session riding,縮寫(xiě)為:CSRF/XSRF。

在同源策略影響下,域名A向域名B發(fā)送Ajax請(qǐng)求,或操作Cookie、LocalStorage、indexDB等數(shù)據(jù),或操作dom,js就會(huì)受到限制,但請(qǐng)求css,js等靜態(tài)資源不受限制

跨域的解決方案

1 通過(guò)jsonp跨域

首先說(shuō)一下jsonp的原理,例如我們平時(shí)寫(xiě)html的時(shí)候常常會(huì)使用

<script src="www.b.com/js/jquery.js"></script>這種方式去取放在另外服務(wù)器上的靜態(tài)資源,這個(gè)是不受同源策略所限制的,所以我們利用這一點(diǎn)可以解決跨域的問(wèn)題。

主要代碼如下:

1.1原生實(shí)現(xiàn) 

  1. 在www.a.com域名寫(xiě)下如下代碼,去請(qǐng)求www.b.com域名的數(shù)據(jù)  
  2. <script>  
  3.     var script = document.creatElement('script');  
  4.     script.type = 'text/javascript' 
  5.     script.src = 'http://www.b.com/getdata?callback=demo'  
  6.     function demo(res){  
  7.       console.log(res);  
  8.     }  
  9. </script> 

這里,我們利用動(dòng)態(tài)腳本的src屬性,變相地發(fā)送了一個(gè)http://www.b.com/getdata?call...。這時(shí)候,b.com頁(yè)面接受到這個(gè)請(qǐng)求時(shí),如果沒(méi)有JSONP,會(huì)正常返回json的數(shù)據(jù)結(jié)果,像這樣:{ msg: 'helloworld' },而利用JSONP,服務(wù)端會(huì)接受這個(gè)callback參數(shù),然后用這個(gè)參數(shù)值包裝要返回的數(shù)據(jù):demo({msg: 'helloworld'});

這時(shí)候,如果a.com的頁(yè)面上正好有一個(gè)demo 的函數(shù):

  1. function demo(res){  
  2. console.log(res);   

當(dāng)遠(yuǎn)程數(shù)據(jù)一返回的時(shí)候,隨著動(dòng)態(tài)腳本的執(zhí)行,這個(gè)demo函數(shù)就會(huì)被執(zhí)行。

1.2 jquery ajax請(qǐng)求實(shí)現(xiàn) 

  1. $.ajax({  
  2.     url:'http://www.b.com/getdata',  
  3.     type:'get',  
  4.     dataType: 'jsonp',  // 請(qǐng)求方式為jsonp  
  5.     jsonpCallback: 'demo', // 自定義回調(diào)函數(shù)名  
  6.     data: {}  
  7. }); 

服務(wù)端代碼實(shí)現(xiàn):

以nodejs為例 

  1. var http = require(http);  
  2. //引入url模塊解析url字符串  
  3. var url = require('url);  
  4. //引入querystring模塊處理query字符串  
  5. var querystring = require('querystring');  
  6. var server = http.createServer();  
  7. server.on('request',function(req,res){  
  8.     var urlurlPath = url.parse(req.url).pathname;  
  9.     var param = querystring .parse(req.url.split('?')[1]);   
  10.     if(urlPath === '/getData' && param.callback) {    
  11.         res.writeHead(200,{'Content-Type','application/json;charset=utf-8'});   
  12.  
  13.         var data = { msg: 'helloworld' };  
  14.         data = JSON.stringify(data );      
  15.  
  16.         var callback = param .callback+'('+data+');';  
  17.         res.write(callback);      
  18.  
  19.         res.end();  
  20.     } else {  
  21.         res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});       
  22.  
  23.         res.write('Hell World\n');  
  24.         res.end();     
  25.  
  26.     }  
  27. }) 

jsonp缺點(diǎn):只能使用get請(qǐng)求,不推薦使用

2 CORS 跨域資源共享

跨域資源共享(CORS) 是一種機(jī)制,它使用額外的 HTTP 頭來(lái)告訴瀏覽器 讓運(yùn)行在一個(gè) origin (domain) 上的Web應(yīng)用被準(zhǔn)許訪問(wèn)來(lái)自不同源服務(wù)器上的指定的資源。當(dāng)一個(gè)資源從與該資源本身所在的服務(wù)器不同的域或端口請(qǐng)求一個(gè)資源時(shí),資源會(huì)發(fā)起一個(gè)跨域 HTTP 請(qǐng)求。

Cross-Origin Resource Sharing跨域資源共享,應(yīng)該算是現(xiàn)在比較推薦的跨域處理方案.不僅適用于各種Method,而且更加方便和簡(jiǎn)單

目前,所有瀏覽器都支持該功能,IE瀏覽器不能低于IE10。

2.1 簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求

瀏覽器將CORS請(qǐng)求分成兩類(lèi):簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)。

簡(jiǎn)單請(qǐng)求同時(shí)滿(mǎn)足以下條件,只要不滿(mǎn)足以下條件的則為非簡(jiǎn)單請(qǐng)求

非簡(jiǎn)單請(qǐng)求會(huì)發(fā)出一次預(yù)檢測(cè)請(qǐng)求,返回碼是204,預(yù)檢測(cè)通過(guò)才會(huì)真正發(fā)出請(qǐng)求,這才返回200。這里通過(guò)前端發(fā)請(qǐng)求的時(shí)候增加一個(gè)額外的headers來(lái)觸發(fā)非簡(jiǎn)單請(qǐng)求。

2.2 進(jìn)行帶有身份憑證的CORS 請(qǐng)求

  •  默認(rèn)情況下的跨域請(qǐng)求都是不會(huì)把cookie發(fā)送給服務(wù)器的,在需要發(fā)送的情況下,如果是xhr,那么需要設(shè)置xhr.withCredentials=true,
  •  如果是采用fetch獲取的話,那么需要在request里面設(shè)置 credentials:'include',
  •  但是如果服務(wù)器在預(yù)請(qǐng)求的時(shí)候沒(méi)返回Access-Control-Allow-Crenditials:true的話,那么在實(shí)際請(qǐng)求的時(shí)候,cookie是不會(huì)被發(fā)送給服務(wù)器端的,要特別注意對(duì)于簡(jiǎn)單的get請(qǐng)求,不會(huì)有預(yù)請(qǐng)求的過(guò)程,
  •  那么在實(shí)際請(qǐng)求的時(shí)候,如果服務(wù)器沒(méi)有返回Access-Control-Allow-Crenditials:true的話那么響應(yīng)結(jié)果瀏覽器也不會(huì)交給請(qǐng)求者

對(duì)于附帶身份憑證的請(qǐng)求,服務(wù)器不得設(shè)置 Access-Control-Allow-Origin 的值為“*”。

這是因?yàn)檎?qǐng)求的首部中攜帶了 Cookie 信息,如果 Access-Control-Allow-Origin的值為“*”,請(qǐng)求將會(huì)失敗。而將 Access-Control-Allow-Origin 的值設(shè)置為http://www.a.com,則請(qǐng)求將成功執(zhí)行。

2.3 HTTP 響應(yīng)首部字段

  •  Access-Control-Allow-Origin: <origin> | *
  •  Access-Control-Expose-Headers 頭讓服務(wù)器把允許瀏覽器訪問(wèn)的頭放入白名單
  •  Access-Control-Max-Age 頭指定了preflight請(qǐng)求的結(jié)果能夠被緩存多久
  •  Access-Control-Allow-Credentials

    頭指定了當(dāng)瀏覽器的credentials設(shè)置為true時(shí)是否允許瀏覽器讀取response的內(nèi)容。

  •  Access-Control-Allow-Methods 首部字段用于預(yù)檢請(qǐng)求的響應(yīng)。其指明了實(shí)際請(qǐng)求所允許使用的 HTTP 方法。
  •  Access-Control-Allow-Headers 首部字段用于預(yù)檢請(qǐng)求的響應(yīng)。其指明了實(shí)際請(qǐng)求中允許攜帶的首部字段。

2.4 以nodejs express為例,說(shuō)明如何使用cors解決跨域 

  1. var express=require('express');  
  2. var url=require('url');  
  3. var app=express();  
  4. var allowCrossDomain = function(req, res, next) {  
  5.     res.header('Access-Control-Allow-Origin', 'http://localhost:63342');  
  6.     res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');  
  7.     res.header('Access-Control-Allow-Headers', 'Content-Type');  
  8.     res.header('Access-Control-Allow-Credentials','true');  
  9.     next();  
  10. };  
  11. app.use(allowCrossDomain);  
  12. app.get('/getData',function (req,res,next) {  
  13.     var queryValue=url.parse(req.url).query;  
  14.     if(queryValue==='fortunewheel@sina.com'){  
  15.         res.send(true);  
  16.     }else {  
  17.         res.send(false);  
  18.     }  
  19. });  
  20. app.listen(3001); 

實(shí)際開(kāi)發(fā)過(guò)程中,為了安全,會(huì)和token一起使用

3 window.postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是為數(shù)不多可以跨域操作的window屬性之一,它可用于解決以下方面的問(wèn)題:

  •  iframe嵌套頁(yè)面跨域通信
  •  頁(yè)面和其打開(kāi)的新窗口的通信
  •  多窗口之間消息傳遞

用法:

postMessage(data,origin)方法接受兩個(gè)參數(shù),

data:需要傳遞的數(shù)據(jù),html5規(guī)范支持任意基本類(lèi)型或可復(fù)制的對(duì)象,但部分瀏覽器只支持字符串,所以傳參時(shí)最好用JSON.stringify()序列化。

origin:協(xié)議+主機(jī)+端口號(hào),也可以設(shè)置為"*",表示可以傳遞給任意窗口,如果要指定和當(dāng)前窗口同源的話設(shè)置為"/"。

代碼示例:

http://www.a.com/a.html 

  1. <iframe id="iframe" src="http://www.b.com/b.html" style="display:none;"></iframe>  
  2. <script>         
  3.     var iframe = document.getElementById('iframe');  
  4.     iframe.onload = function() {  
  5.         var data = {  
  6.             name: 'jianjian'  
  7.         };  
  8.         // 向http://www.b.com傳送跨域數(shù)據(jù)  
  9.         iframe.contentWindow.postMessage(JSON.stringify(data),'http://www.b.com');  
  10.     };  
  11.     // 接受http://www.b.com返回?cái)?shù)據(jù)  
  12.     window.addEventListener('message', function(e) {  
  13.         alert('data from http://www.b.com---> ' + e.data);  
  14.     }, false);  
  15. </script> 

http://www.b.com/b.html 

  1. <script>  
  2.     // 接收http://www.a.com/a.html的數(shù)據(jù)  
  3.     window.addEventListener('message', function(e) {  
  4.         alert('data from http://www.a.com/a.html---> ' + e.data);  
  5.         var data = JSON.parse(e.data);  
  6.         if (data) {  
  7.             data.number = 16 
  8.             // 處理后再發(fā)回http://www.a.com/a.html  
  9.             window.parent.postMessage(JSON.stringify(data), 'http://www.a.com');  
  10.         }  
  11.     }, false);  
  12. </script> 

4 document.domain

這種方式只適合主域名相同,但子域名不同的iframe跨域。

實(shí)現(xiàn)原理:兩個(gè)頁(yè)面都通過(guò)js強(qiáng)制設(shè)置document.domain為基礎(chǔ)主域,就實(shí)現(xiàn)了同域。

使用方式:

http://www.a.com/a.html 

  1. <iframe id="iframe" src="http://www.child.a.com/b.html" style="display:none;"></iframe>  
  2. <script>    
  3.       document.domain = 'a.com'  
  4.       var a = 'hello world'  
  5. </script> 

"http://www.child.a.com/b.html 

  1. <script>     
  2.       document.domain = 'a.com'  
  3.       var b = window.parent.a;    
  4.       console.log(b);  
  5. </script> 

5 window.name

window.name 傳輸技術(shù)的基本原理:

當(dāng)在瀏覽器中打開(kāi)一個(gè)頁(yè)面,或者在頁(yè)面中添加一個(gè)iframe時(shí)即會(huì)創(chuàng)建一個(gè)對(duì)應(yīng)的window對(duì)象,當(dāng)頁(yè)面加載另一個(gè)新的頁(yè)面時(shí),window.name的屬性是不會(huì)變的。這樣就可以利用在頁(yè)面動(dòng)態(tài)添加一個(gè)iframe然后加載數(shù)據(jù)頁(yè)面,在數(shù)據(jù)頁(yè)面將需要的數(shù)據(jù)賦值給window.name。然而此時(shí)承載的iframe的parent頁(yè)面還是不能直接訪問(wèn)不在同一域下的iframe的那么屬性,這時(shí),只需要將iframe再加載一個(gè)與承載頁(yè)面同域的空白頁(yè)面,即可對(duì)window.name進(jìn)行數(shù)據(jù)讀取。

通過(guò)iframe的src屬性由外域轉(zhuǎn)向本地域,跨域數(shù)據(jù)即由iframe的window.name從外域傳遞到本地域。這個(gè)就巧妙地繞過(guò)了瀏覽器的跨域訪問(wèn)限制,但同時(shí)它又是安全操作。

具體實(shí)現(xiàn):

http://www.a.com/a.html 主頁(yè)面

http://www.b.com/b.html 數(shù)據(jù)頁(yè)面

http://www.a.com/proxy.html 代理頁(yè)面

http://www.a.com/a.html代碼: 

  1. <script>   
  2. function crosDomainGetData(url,callback){  
  3.     var state = 0 
  4.     var iframe = document.createElement('iframe);  
  5.     iframe.src = url;      
  6.     iframe.onload = function(){  
  7.         if(state === 1){  
  8.            //代理頁(yè)面成功過(guò)后,讀取window.name  
  9.             var data = iframe.contentWindow.name;  
  10.             callback&&callback(data);   
  11.  
  12.             //銷(xiāo)毀iframe  
  13.             iframe.contentWindow.document.write('');  
  14.             iframe.contentWindow.close();  
  15.             document.body.removeChild(iframe);  
  16.  
  17.         } else {  
  18.             //第一次加載數(shù)據(jù)頁(yè)面成功后,切換代理頁(yè)面  
  19.             state = 1 
  20.             iframe.contentWindow.location = 'http://www.a.com/proxy.html' 
  21.         }  
  22.     }      
  23.     document.body.appendChild(iframe);  
  24.  
  25. crosDomainGetData('http://www.b.com/b.html',function(data){  
  26.     alert(data);  
  27. })  
  28. </script> 

http://www.b.com/b.html代碼: 

  1. window.name = '123' 

http://www.a.com/proxy.html空白

6 nginx代理跨域 

  1. server{  
  2.     # 監(jiān)聽(tīng)8080端口  
  3.     listen 8080;  
  4.     # 域名是localhost  
  5.     server_name localhost;  
  6.     #凡是localhost:8080/api這個(gè)樣子的,都轉(zhuǎn)發(fā)到真正的服務(wù)端地址http://www.b.com:8080   
  7.     location ^~ /api {  
  8.         proxy_pass http://www.b.com:8080;  
  9.     }      

配置之后就不需要前端做什么修改了,一般我們?cè)谇昂蠖朔蛛x項(xiàng)目中開(kāi)發(fā)階段會(huì)采用這種方式,但不是所有場(chǎng)景都能這樣做,例如后端接口是一個(gè)公共的API,比如一些公共服務(wù)獲取天氣什么的。

7 WebSocket協(xié)議跨域

websoket協(xié)議天然支持跨域,你只需要學(xué)會(huì)如何使用它即可,關(guān)于websocket協(xié)議請(qǐng)看我的另外一篇文章WebSocket網(wǎng)絡(luò)通信協(xié)議

責(zé)任編輯:龐桂玉 來(lái)源: segmentfault
相關(guān)推薦

2024-05-22 19:10:18

跨域Web開(kāi)發(fā)

2024-05-20 09:28:44

Spring客戶(hù)端瀏覽器

2018-01-26 08:39:03

2023-05-06 15:32:04

2019-11-11 17:34:16

前端開(kāi)發(fā)技術(shù)

2024-12-02 14:30:20

2020-09-17 13:33:39

開(kāi)發(fā)

2022-03-01 09:31:06

JWTSession跨域

2021-06-25 09:04:39

Cors跨域JSONP vs CO

2010-02-24 10:55:01

WCF跨域訪問(wèn)

2010-07-30 12:40:00

Flex跨域訪問(wèn)

2023-11-17 09:38:21

2017-12-19 17:54:31

前端ajax跨域cors

2012-05-30 15:40:16

大并發(fā)并發(fā)解決方案

2010-10-21 21:35:35

聯(lián)網(wǎng)監(jiān)控多域視頻H3C

2012-05-09 10:08:41

跨機(jī)房

2016-11-04 20:02:37

Apache

2012-03-30 16:12:36

惠普IT績(jī)效管理

2009-11-25 13:00:36

上網(wǎng)行為管理解決方案

2011-08-24 11:21:00

深信服APM
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)