深度解析跨域

深度解析跨域

什麼叫跨域?

跨域,指的是瀏覽器不能執行其餘網站的腳本。 它是由瀏覽器的同源策略形成的,是瀏覽器施加的安全限制。 所謂同源策略是指,協議、端口、域名都相同。javascript

形成跨域的兩種策略

瀏覽器的同源策略會致使跨域,這裏同源策略又分爲如下兩種:html

  • DOM同源策略:禁止對不一樣源頁面DOM進行操做。這裏主要場景是iframe跨域的狀況,不一樣域名的iframe是限制互相訪問的。
  • XmlHttpRequest同源策略:禁止使用XHR對象向不一樣源的服務器地址發起HTTP請求。

爲何要有跨域限制

瀏覽器執行腳本時,會檢查腳本是否同源,若是不是,則不會被執行。java

兩個域名不能跨過域名來發送數據和請求數據,不然就是不安全的,這種不安全也就是CSRF(跨站請求僞造)攻擊。jquery

若是沒有AJAX同源策略,至關危險,咱們發起的每一次HTTP請求都會帶上請求地址對應的cookie,CSRF 攻擊是利用用戶的登陸態發起惡意請求。web

如何解決跨域

  1. JSONP
  2. CORS
  3. 服務器代理
  4. document.domain跨子域
  5. location.hash跨域
  6. postMessage

JSONP

JSONP的產生:ajax

  1. AJAX直接請求普通文件存在跨域無權限訪問的問題,不論是靜態頁面也好.json

  2. 不過咱們在調用js文件的時候又不受跨域影響,好比引入jquery框架的,或者是調用相片的時候api

  3. 凡是擁有src這個屬性的標籤均可以跨域例如<script> <img> <iframe>跨域

  4. 若是想經過純web端跨域訪問數據只有一種可能,那就是把遠程服務器上的數據裝進js格式的文件裏.瀏覽器

  5. 而json又是一個輕量級的數據格式,還被js原生支持

  6. 爲了便於客戶端使用數據,逐漸造成了一種非正式傳輸協議,人們把它稱做JSONP,該協議的一個要點就是容許用戶傳遞一個callback 參數給服務端。

基於script跨域

<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
    function jsonp(data) {
    	console.log(data)
	}
</script> 
複製代碼

在開發中可能會遇到多個 JSONP 請求的回調函數名是相同的,這時候就須要本身封裝一個 JSONP,如下是簡單實現:

function jsonp(url, jsonpCallback, success) {
  let script = document.createElement('script')
  script.src = url
  script.async = true
  script.type = 'text/javascript'
  window[jsonpCallback] = function(data) {
    success && success(data)
  }
  document.body.appendChild(script)
}
jsonp('http://xxx', 'callback', function(value) {
  console.log(value)
})
複製代碼

基於jQuery跨域

$.ajax({
    url:'http://domain/api',
    ype : 'get',
    dataType : 'text',
    success:function(data){
        alert(data);
    },
    error:function(data){
        alert(2);
    }        
});
複製代碼

經過iframe來跨子域

eg: a.study.cn/a.html 請求 b.study.cn/b.html

a.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
        <script type="text/javascript">
            document.domain = 'study.cn';
            function test() {
                alert(document.getElementById('a').contentWindow);
            }
        </script>
</head>
<body>
    <iframe id='a' src='http://b.study.cn/b.html' onload='test()'>
</body>
</html>
複製代碼

b.html

<!DOCTYPE html>
<html>
    <head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
        <script type="text/javascript">
        document.domain = 'study.cn';
        </script>
    </head>
    <body>
        我是b.study.cn的body
    </body>
</html>
複製代碼

修改document.domain的方法只適用於不一樣子域的框架(父類與子類)間的交互。

CORS

支持CORS請求的瀏覽器一旦發現ajax請求跨域,會對請求作一些特殊處理,對於已經實現CORS接口的服務端,接受請求,並作出迴應。

有一種狀況比較特殊,若是咱們發送的跨域請求爲「非簡單請求」,瀏覽器會在發出此請求以前首先發送一個請求類型爲OPTIONS的「預檢請求」,驗證請求源是否爲服務端容許源,這些對於開發這來講是感受不到的,由瀏覽器代理。

總而言之,客戶端不須要對跨域請求作任何特殊處理。

瀏覽器對跨域請求區分爲「簡單請求」與「非簡單請求」

簡單請求

(1) 請求方法是如下三種方法之一:

HEAD

 GET

 POST
複製代碼

(2)HTTP的頭信息不超出如下幾種字段:

Accept

 Accept-Language

 Content-Language

 Last-Event-ID

 Content-Type:
     application/x-www-form-urlencoded、 multipart/form-data、text/plain
複製代碼

不知足這些特徵的請求稱爲「非簡單請求」,例如:content-type=applicaiton/json , method = PUT/DELETE...

瀏覽器判斷跨域爲簡單請求時候,會在Request Header中添加 Origin (協議 + 域名 + 端口)字段 , 它表示咱們的請求源,CORS服務端會將該字段做爲跨源標誌。

CORS接收到這次請求後 , 首先會判斷Origin是否在容許源(由服務端決定)範圍以內,若是驗證經過,服務端會在Response Header 添加 Access-Control-Allow-Origin、Access-Control-Allow-Credentials等字段。

必須字段:
Access-Control-Allow-Origin:表示服務端容許的請求源,*標識任何外域,多個源 , 分隔

可選字段
Access-Control-Allow-Credentials:false 表示是否容許發送Cookie,設置爲true
                                 同時,ajax請求設置withCredentials = true,瀏覽
                                 器的cookie就能發送到服務端

Access-Control-Expose-Headers:調用getResponseHeader()方法時候,能從header中獲
                               取的參數
複製代碼

總結:簡單請求只須要CORS服務端在接受到攜帶Origin字段的跨域請求後,在response header中添加Access-Control-Allow-Origin等字段給瀏覽器作同源判斷

進行非簡單請求時候, 瀏覽器會首先發出類型爲OPTIONS的「預檢請求」,請求地址相同 , CORS服務端對「預檢請求」處理,並對Response Header添加驗證字段,客戶端接受到預檢請求的返回值進行一次請求預判斷,驗證經過後,主請求發起。

例如:發起 content-type=application/json 的非簡單請求,這時候傳參要注意爲json字符串

這裏能夠看到,瀏覽器連續發送了兩個jsonp.do請求 , 第一個就是「預檢請求」,類型爲OPTIONS,由於咱們設置了content-type這個屬性,因此預檢請求的Access-Control-Expose-Headers必須攜帶content-type,不然預檢會失敗。

預檢經過後,主請求與簡單請求一致。

總結:非簡單請求須要CORS服務端對OPTIONS類型的請求作處理,其餘與簡單請求一致

經過上面敘述,咱們得知藉助CORS咱們沒必要關心發出的請求是否跨域,瀏覽器會幫咱們處理這些事情,可是服務端須要支持CORS,服務端實現CORS的原理也很簡單,在服務端徹底能夠對請求作上下文處理,已達到接口容許跨域訪問的目的。

服務器代理

瀏覽器有跨域限制,可是服務器不存在跨域問題,因此能夠由服務器請求所要域的資源再返回給客戶端。

document.domain

該方式只能用於二級域名相同的狀況下,好比 a.test.com 和 b.test.com 適用於該方式。

只須要給頁面添加 document.domain = 'test.com' 表示二級域名都相同就能夠實現跨域。

location.hash跨域

location.hash方式跨域,是子框架具備修改父框架src的hash值,經過這個屬性進行傳遞數據,且更改hash值,頁面不會刷新。可是傳遞的數據的字節數是有限的。

postMessage

頁面和新開的窗口的數據交互。

  • 多窗口之間的數據交互。
  • 頁面與所嵌套的iframe之間的信息傳遞。
  • window.postMessage是一個HTML5的api,容許兩個窗口之間進行跨域發送消息。

這個應該就是之後解決dom跨域通用方法了,具體能夠參照MDN。

// 發送消息端
window.parent.postMessage('message', 'http://test.com')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
  var origin = event.origin || event.originalEvent.origin
  if (origin === 'http://test.com') {
    console.log('驗證經過')
  }
})
複製代碼

參考:

跨域——CORS詳解

跨域的那些事兒

相關文章
相關標籤/搜索