ajax跨域請求原理及解決方案分析

##1. 什麼是跨域(Cross-site)?
想了解跨域,必須先了解一下「同源策略(same origin policy)」。javascript

1.1 同源策略

它限制了某個域下的文檔或者js與另外一個域中的資源交互的方式,它提供了一種安全機制,這種安全機制能夠避免來自惡意網站的攻擊。 同源策略要求瀏覽器容許來自某個網頁上的js請求來自另外一個網頁的數據,當且僅當兩個頁面來自相同的域。css

1.2 什麼是域(origin)?

域是由三部分組合而成:URI Schema(協議類型),host name(域名),port number(端口號)
舉個例子:
1) http://www.domain.com 這個頁面,URI Schema是http,host name是www.domain.com,port number是默認的80
2) https://www.xxx.com:8080/xxx/yyy URI Schema是https,hostname是www.xxx.com,port number是8080
因爲1)和2)中的三部分都不相同,因此它們就是不一樣的域。 下面的圖更好的解釋了什麼是同域:
QQ圖片20151224163925.png-15.9kB
PS:IE瀏覽器裏可能不太同樣,它不會把端口號做爲判斷依據。html

1.3 爲何要有同源策略?

提出同源策略的目的是出於安全性考慮,它可以阻止來自惡意網站的腳本經過其餘網站的DOM獲取其餘網站的信息。能夠避免CSRF和XSS攻擊。前端

1.4 同源策略是限制誰的?

1) 不少人可能搞不清楚這個問題,同源策略限制的是瀏覽器或者其餘提供相似瀏覽器服務的軟件,並且這僅僅是個規範,因此瀏覽器是否遵照這個規範也不必定,因此就會有上面的IE瀏覽器判斷是否同源的時候並無考慮端口號的問題。 2) 同源策略限制的是js,而圖片,css這些是不存在同源策略限制的。java

1.5 什麼是跨域?

在某個網站的頁面上經過js請求另一個網站的數據,若是兩個網站不知足同源策略,那麼就存在跨域問題。nginx

2. 爲何會有跨域問題?

因爲在實際環境中,常常須要經過js獲取一些數據,特別是ajax的流行,經過ajax加載某個網站的數據的場景就會常常遇到,而一旦有這樣的需求,就可能會出現跨域的問題。ajax

3. 如何判斷我是否遇到了跨域問題?

通常來說,若是你的請求被同源策略限制,瀏覽器的開發工具都會給出錯誤提示,在Chrome瀏覽器的console中,可能會有相似下面的提示:
QQ截圖20151224171533.png-110.1kBjson

4.如何解決跨域問題?

通常的思路是:經過一些妥協調整,繞過同源策略的限制。下面是我最近了解的一些方法。
爲方便講解,這裏先舉一個例子:
客戶端採用H5開發,全部的數據都經過ajax請求從服務端獲取。
客戶端的頁面都存放在靜態文件服務器中,域名是http://static.demo.com
服務端提供接口供客戶端調用,接口的參數和返回值都是JSON格式,服務端的域名是:http://server.demo.com。
若是不考慮跨域的問題,客戶端與服務端的交互方式以下:
1.客戶端post請求服務端,參數:{"key":"value"}
2.服務端返回結果:{"code":1,"data":"success"}後端

4.1 Jsonp方式

原理: 經過在頁面中新增一個<script>標籤,標籤的src指向的是另一個域的可以提供數據的url,同時將一個本地的callback方法傳給服務端,服務端返回的時候將會自動執行callback方法。
實現舉例:
1)服務端修改返回的數據類型爲js,同時在請求參數中增長一個callback字段,這個字段用於客戶端傳遞要執行的js方法名稱。 2)客戶端傳遞的參數中增長callback,同時將普通的ajax方法改爲在頁面中新增一個<script>節點的方式。 具體實現:跨域

1.經過js在頁面中append以下標籤

<script type="application/javascript"
        src="http://server.demo.com/Users/1234?callback=parseResponse"></script>

增長該標籤以後,瀏覽器就會當即去請求這個url,因爲<script src="">方式的是不受同源策略限制的,因此能夠避免跨域限制。

2.服務端收到callback參數以後,將它拼接在返回的數據中,返回的數據以下:

parseResponse({"Name": "Foo", "Id": 1234, "Rank": 7});

3.這樣返回以後,就調用頁面上的parseResponse js方法,就達到了數據處理的目的。
4.最後將剛剛新增長到頁面中的<script>元素刪掉。

4.2 設置document.domain屬性

若是兩個頁面或者frame能夠將document.domain屬性設置成相同的值,那麼也能夠繞過同源策略限制。 假設兩個頁面分別是static.demo.com和server.demo.com,兩個頁面加載以後都經過js將document.domain設置成demo.com,這樣接下來的ajax請求就能夠繞過同源策略限制了。
可是:若是兩個頁面存在端口,好比static.demo.com:8080 和 server.demo.com:8090,因爲document.domain只能設置域名,因此就不起做用。
舉例:
上面的例子,因爲服務端返回的是json,而不是一個頁面,因此無法將本身的域名設置成demo.com,可是能夠經過另一種方式,即在服務端增長一個靜態頁面,頁面中放以下js代碼:

document.domain=demo.com

或者以下代碼:

try{document.domain = window.location.hostname.split('.').reverse().slice(0,2).reverse().join('.');}catch(e){}

而後客戶端頁面加載的時候先去調用一下這個靜態頁面就行了。

4.3 CORS(Cross-Origin Resource Sharing)

原理MDN上講的更清楚一些,點擊這裏。 其實簡單來講就是服務端在響應頭中添加一個Access-Control-Allow-Origin頭部,頭部的值爲客戶端的域名,好比:http://static.demo.com,這樣就能夠了。
可是須要注意的是:CROS分爲兩種,一種是簡單請求,一種是複雜請求,簡單請求按照上面的方式是能夠的,若是是複雜請求,瀏覽器會進行兩步,先發一個options請求,這個請求稱之爲「預請求」,預請求其實是個OPTIONS請求,相似於一個探測做用,若是服務端返回的頭部經過了預請求的內容,則瀏覽器纔會發起第二個真實請求。這個後續我會有詳細文章介紹。

4.4 客戶端請求經過Nginx轉發

原理:客戶端的全部請求都直接發到客戶端所在域名下,可是在客戶端服務器增長一臺nginx服務器,做爲代理,若是是後端的url,直接代理轉發到服務端,這樣就不存在前端的跨域問題了。
舉例:

server {
    listen       80;
    server_name  static.demo.com;    #可配置多個主機頭
    charset utf-8,gbk,gb2312,gb18030; #能夠實現多種編碼識別

    location / {
        root   /home/wy/www/static.demo.com/ROOT;  #網站文件路徑
        
        autoindex on;
        autoindex_exact_size off;
        autoindex_localtime on;
        index  default.html;
    }
    #全部/server/開頭的請求都會走這裏
    location /server/ {
            proxy_pass http://server.demo.com:8080;  ##轉發到server
            proxy_set_header    Host             $host;
            proxy_set_header    X-Real-IP        $remote_addr;
            proxy_set_header    X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
}

4.5 其餘方式

1)WebSocket 2)Cross-document messaging

[參考資料]

  1. https://en.wikipedia.org/wiki/Same-origin_policy
  2. https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
  3. https://en.wikipedia.org/wiki/Cross-site_request_forgery
  4. https://en.wikipedia.org/wiki/Cross-site_scripting
  5. https://en.wikipedia.org/wiki/JSONP
  6. http://www.w3.org/TR/cors/
相關文章
相關標籤/搜索