Web学习之跨域问题及解决方案
Web學習之跨域問題及解決方案
javascript/jquery瀏覽數:161
2017-5-8
在做前端開發時,我們時常使用ajax與服務器通信獲取資源,享受ajax便利的同時,也知道它有限制:跨域安全限制,即同源策略。
 同源策略(SOP),核心是確保不同源提供的文件之間是相互獨立的
 默認情況下,XHR對象只能訪問與包含它的頁面處于同一域中的資源,這種限制可以預防某些惡意攻擊,但同時也帶來很多不便。
本篇對于常見的解決瀏覽器跨域問題的方案進行總結闡述。
常見解決跨域問題的方案
在web開方中,解決跨域問題最常見的方法有:
- document.domain+iframe(子域名代理)
- jsonp實現跨域
- CORS跨域資源共享
- postMessage實現跨域
接下來對以上常用方式進行詳細闡述:
document.domain + iframe(子域名代理)
對于主域相同而子域不同的情況,可以通過設置document.domain的辦法來解決。如:對于兩個文件http://www.a.com/a.html和http://blog.a.com/b.html均加上設置document.domain = ‘a.com’;然后在a.html文件中創建一個iframe,通過iframe兩個js文件即可交互數據:
| //www.a.com/a.html ????<script> ????????document.doamin = 'a.com'; ????????var iframe = document.createElement('iframe'); ????????iframe.src = 'http://blog.a.com/b.html'; ????????document.body.appendChild(iframe); ????????iframe.onload = function() { ????????????var doc = iframe.contentDocument || iframe.contentWindow.document; ????????????// 在這里操縱b.html ????????????console.log(doc); ????????}; ????</script> | 
在blog.a.com/b.html內編寫代碼:
| //blog.a.com/b.html ????<script> ????????document.domain = 'a.com'; ????</script> | 
 備注:某一頁面的domain默認等于window.location.hostname。主域名是不帶www的域名,例如a.com,主域名前
 面帶前綴的通常都為二級域名或多級域名,例如blog.a.com其實是二級域名。 domain只能設置為主域名,不可以在
 blog.a.com中將domain設置為test.a.com。
 問題:
 1、安全性,當一個站點(b.a.com)被攻擊后,另一個站點(c.a.com)會引起安全漏洞。
 2、如果一個頁面中引入多個iframe,要想能夠操作所有iframe,必須都得設置相同domain。
jsonp實現跨域
 JSONP,即帶填充的JSON,可以在JavaScript中繞過同源策略,發起跨域HTTP請求。
 JSONP,一個無關標準,使用腳本標簽來跨域獲取數據的技術。
 同源策略有一個顯著例外:HTML腳本元素是可以規避SOP檢查的。這意味著我們可以通過加載外部JavaScript文件的方式來向其他源發出HTTP請求。
| <script>http://blog-resource.bj.bcebos.com/files/2016/03/info.js</script> | 
| //http://blog-resource.bj.bcebos.com/files/2016/03/info.js ???var jsonpData = { ???????name: 'jsonp', ???????title: 'JSONP data' ???}; | 
動態的回調函數
JSONP通過script元素加載JSON,通過腳本URL的查詢字符串,服務器將響應封裝在回調函數中,而回調函數名由請求者在URL查詢字符串中給出,此回調函數,即填充。填充可以是任何有效的JavaScript表達式,最常見的是變量和回調函數。
| <script> ????????var jsonpCallback = function(data) { ????????????console.log('The response data: ' + JSON.stringify(data)); ????????}; ????????var script = document.createElement('script'); ????????script.async = true; ????????script.src = 'http://blog-resource.bj.bcebos.com/files/2016/03/info2.js?callback=jsonpCallback'; ????????document.body.appendChild(script); ????</script> | 
在全局作用域下定義回調函數,當HTML DOM中加入script標簽時發起請求,服務端可以通過URL獲取回調函數名,并生成返回如下類似內容:
| jsonpCallback({ ???????name: 'jsonp', ???????title: 'JSONP data' ???}); | 
如php實現:
| <!--?php ??????header("Content-type: application/javascript"); ??????$callback = $_GET['callback']; ??????$data = json_encode(array( ??????????'name' =--> 'jsonp, ??????????'title' => 'JSONP data' ??????), JSON_PRETTY_PRINT); ??????echo "$callback($data);"; ???> | 
jQuery利用jsonp發送跨域請求:
| $.ajax({??? ??????????url: '',? // 跨域URL?? ??????????type: 'GET',??? ??????????dataType: 'jsonp',??? ??????????jsonp: 'jsonpcallback', //默認callback?? ??????????data: mydata, //請求數據?? ??????????timeout: 5000, ??????????success: function (json) { //客戶端jquery預先定義好的callback函數,成功獲取跨域服務器上的 ???json數據后,會動態執行這個callback函數??? ??????????????if(json.actionErrors.length!=0){??? ??????????????????alert(json.actionErrors);??? ??????????????}??? ??????????},??? ??????????complete: function(XMLHttpRequest, textStatus){??? ??????????} ??????}); | 
JSONP可以說是最簡單的解決跨域請求的方案,但JSONP只支持GET請求,且所能攜帶信息量有限,對于需要使用POST等請求方法或數據量比較大的時候就不適合使用JSONP。
跨域資源共享(CORS)
跨域資源共享(CORS)定義了瀏覽器和服務器如何通過可控方式進行跨域通信。CORS通過添加特殊HTTP頭信息以允許瀏覽器和服務器判斷請求是成功還是失敗。幾乎所有現代瀏覽器都支持CORS。
 HTTP請求
 當跨域發送HTTP請求時,支持CORS的瀏覽器會通過,添加額外Origin頭信息指定請求源,其值包括請求協議、域名、端口。如:
| Origin: http://www.codingplayboy.com | 
若請求頭中無Origin信息,服務器將不返回任何CORS頭信息。
服務端接收到請求時會檢查頭信息確定是否接受請求:若接受請 求,必須返回一個包含Access-Control-Allow-Origin響應頭,其值與請求頭Origin值相同,如:
| Access-Control-Allow-Origin: http://www.codingplayboy.com | 
對于公共資源且允許任何源請求數據,服務器通常返回該值為通配符*,如:
| Access-Control-Allow-Origin: * | 
瀏覽器接收到服務器HTTP響應時,首先會檢查Access-Control-Allow-Origin的值,只有當值存在且同Origin匹配,才繼續處理該請求。
下面是一段使用CORS跨域請求的代碼:
| function createCORS(url, method) { ????if (typeof XMLHttpRequest === 'undefined') { ????????return null; ????} ????//標準瀏覽器 ????var xhr = new XMLHttpRequest(); ????if ('withCredentials' in xhr) { ????????xhr.open(method, url, true); ????}else if (typeof XDomainRequest !== 'undefined') { ????????//IE ????????xhr = new XDomainRequest(); ????????xhr.open(method, null); ????}else { ????????//不支持CORS ????????xhr = null; ????} ????return xhr; } var req = createCORS('http://blog.codingplayboy.com', 'GET'); if (req) { ????req.onload = function() { ????}; ????req.send(); } | 
 Cookie及HTTP認證頭
 我們還需要知道不同于普通的HTTP請求,使用CORS發送請求時,默認情況下,瀏覽器不會攜帶任何Cookie和HTTP認證頭等識別信息,只有當我們將XMLHttpRequest對象的withCredentials屬性值設為true,才在該請求發送時添加額外識別信息。
| var xhr = new XMLHttpRequest(); xhr.withCredentials = true; | 
若服務器需要識別信息,則在響應頭中添加Access-Control-Allow-Credentials響應頭:
| Access-Control-Allow-Credentials: true | 
若發送請求時將withCredentials設為true,服務端沒有返回該響應頭,瀏覽器將拒絕該響應。
本來一切都是美好的,然而XDomainRequest不支持withCredentials屬性,于是IE8、IE9并不支持請求時包含識別信息。
 預檢請求
 使用CORS時,若請求方法不是GET,POST或HEAD,又或者使用了自定義HTTP頭,瀏覽器將發起預檢請求。
 預檢請求是一個服務端認證機制,在執行請求之前會判斷該請求是否合法。
 發送復雜請求時,瀏覽器會將原始請求的方法和請求頭作為預檢請求的信息發送給服務器;服務器需要決定是否接受該請求并響應,預檢請求通常是使用一種OPTIONS的HTTP方法。
客戶端發起復雜請求時,會先發起預檢,攜帶以下頭信息:
| Origin | 請求源 | 
| Access-Control-Request-Method | 請求方法(HTTP方法) | 
| Access-Control-Request-Headers | 請求自定義頭(以逗號分隔) | 
服務器返回的響應頭:
| Access-Control-Allow-Origin | 允許的請求源(與請求頭Origin匹配) | 
| Access-Control-Allow-Methods | 允許的請求方法(以逗號分隔) | 
| Access-Control-Allow-Headers | 允許的頭信息(以逗號分隔) | 
| Access-Control-Max-Age | 預檢請求的緩存時間(單位:秒) | 
| Access-Control-Allow-Credentials | 指定請求是否支持認證信息(可選) | 
客戶端接收到服務端的響應后,會使用之前聲明的HTTP方法和請求頭發送真正的請求。
預檢請求會被瀏覽器緩存,緩存時間為Access-Control-Max-Age值,緩存時間內,后續相同類型的請求不再發起重復的預檢請求。IE8、IE9均不支持預檢請求。
HTML5 postMessage
HTML5的window.postMessage為瀏覽器帶來了一個安全的、基于事件的消息API。與之前的子域名代理通過iframe跨子域通信不同,使用postMessage不再是直接訪問一個文檔的屬性和方法,而是向文檔發送消息然后等待響應,這要求形成一條雙向的通信通道。
原文鏈接:http://blog.codingplayboy.com/2016/03/05/web_cross_origin/
總結
以上是生活随笔為你收集整理的Web学习之跨域问题及解决方案的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: VueJS 开发常见问题集锦
- 下一篇: 【JVM】如何选择垃圾回收器?
