天天看點

Ajax跨域通路

最近在做一個項目,需要用到跨域通路,在這裡将解決問題的過程與大家分享一下。

javascript出于安全方面的考慮,使用同源政策,不允許跨域調用其他頁面的對象。但在安全限制的同時也給注入iframe或是ajax應用上帶來了不少麻煩。

舉例如下:

url1

url2

說明

是否允許通信

http://www.a.com/a.js

http://www.a.com/b.js

同一域名下

允許

http://www.a.com/lab/a.js

http://www.a.com/script/b.js

同一域名下不同檔案夾

http://www.a.com:8000/a.js

同一域名,不同端口

不允許

https://www.a.com/b.js

同一域名,不同協定

http://127.0.0.1/b.js

域名和域名對應ip

http://script.a.com/b.js

主域相同,子域不同

http://a.com/b.js

同一域名,不同二級域名(同上)

不允許(cookie這種情況下也不允許通路)

http://www.cnblogs.com/a.js

不同域名

對于跨域的問題,在網上也有很多解決方案,很多人都建議使用<code>jsonp</code>,這種方式我認為需要對服務端進行改造,代價稍高,是以在這篇部落格中我們不讨論,有興趣可以檢視相關文檔,今天我們主要來讨論如何使用響應頭的方式來解決跨域的問題。

首先我們先來看看,跨域到底是個什麼問題,我顯現編寫一個服務端的程式用于測試(使用<code>servlet</code>),

在<code>web.xml</code>中進行相關配置使其生效:

配置本地host為:<code>www.jianggujin.com</code>,使用浏覽器通路<code>http://www.jianggujin.com/web/cd</code>測試一下,浏覽本期會顯示:<code>{"name":"jianggujin"}</code>。

接下來我們在編寫一個本地的網頁:

通路頁面,浏覽器控制台顯示如下資訊(注:顯示新格式可能會不同,以具體環境為準):

<code>[web浏覽器] "xmlhttprequest cannot load http://www.jianggujin.com/web/cd. no 'access-control-allow-origin' header is present on the requested resource. origin 'http://127.0.0.1:8020' is therefore not allowed access." /mui/crossdomain.html (0)</code>

這就是跨域導緻的問題,同樣的位址我們使用浏覽器可以直接通路,但是使用ajax請求的時候,由于同源政策就被限制了,請求不被允許。通過錯誤資訊,我們也可以得到一點解決問題的消息,缺少:<code>access-control-allow-origin</code>頭。

在w3c中對該問題也做了描述,并列舉了一些頭,下述内容摘自原文:

5 syntax this section defines the syntax of the new headers this specification introduces. it also provides a short description of the function of each header. the resource processing model section details how resources are to use these headers in a response. likewise, the user agent processing model section details how user agents are to use these headers. the abnf syntax used in this section is from http/1.1. [http] http/1.1 is used as abnf basis to ensure that the new headers have equivalent parsing rules to those introduced in that specification. http/1.1 currently does not make leading ows implied in header value definitions but that form is assumed here. 5.1 access-control-allow-origin response header the access-control-allow-origin header indicates whether a resource can be shared based by returning the value of the origin request header, “*”, or “null” in the response. abnf: access-control-allow-origin = “access-control-allow-origin” “:” origin-list-or-null | “*” in practice the origin-list-or-null production is more constrained. rather than allowing a space-separated list of origins, it is either a single origin or the string “null”. 5.2 access-control-allow-credentials response header the access-control-allow-credentials header indicates whether the response to request can be exposed when the omit credentials flag is unset. when part of the response to a preflight request it indicates that the actual request can include user credentials. abnf: access-control-allow-credentials: “access-control-allow-credentials” “:” true true: %x74.72.75.65 ; “true”, case-sensitive 5.3 access-control-expose-headers response header the access-control-expose-headers header indicates which headers are safe to expose to the api of a cors api specification. abnf: access-control-expose-headers = “access-control-expose-headers” “:” #field-name 5.4 access-control-max-age response header the access-control-max-age header indicates how long the results of a preflight request can be cached in a preflight result cache. abnf: access-control-max-age = “access-control-max-age” “:” delta-seconds 5.5 access-control-allow-methods response header the access-control-allow-methods header indicates, as part of the response to a preflight request, which methods can be used during the actual request. the <code>allow</code> header is not relevant for the purposes of the cors protocol. abnf: access-control-allow-methods: “access-control-allow-methods” “:” #method 5.6 access-control-allow-headers response header the access-control-allow-headers header indicates, as part of the response to a preflight request, which header field names can be used during the actual request. abnf: access-control-allow-headers: “access-control-allow-headers” “:” #field-name 5.7 origin request header the origin header indicates where the cross-origin request or preflight request originates from. [origin] 5.8 access-control-request-method request header the access-control-request-method header indicates which method will be used in the actual request as part of the preflight request. abnf: access-control-request-method: “access-control-request-method” “:” method 5.9 access-control-request-headers request header the access-control-request-headers header indicates which headers will be used in the actual &gt;request as part of the preflight request. abnf: access-control-request-headers: “access-control-request-headers” “:” #field-name

看不懂?不着急,我們急需解決問題,上面的一大串内容的意思就是提供了這些響應頭用具解決ajax的跨域問題。當ajax進行跨域通路時,浏覽器首先會進行一次option請求判斷該請求是否被循序跨域,當響應結果為允許通路時,再進行真正的請求。

下面我們對servlet進行修改使其允許跨域:

再次打開網頁,觀察控制台輸出:

Ajax跨域通路

通過輸出,我們發現這時候已經可以正常通路了,這樣我們就滿足了嗎?如果有cookie之類的資訊也可以正常送出嗎?我們來繼續測試,在此更改服務端代碼:

我們先用浏覽器直接通路位址,通路兩次,觀察控制台輸出:

Ajax跨域通路

然後我們在通過網頁通路看看結果:

Ajax跨域通路

兩次結果對比,我們可以發現,雖然我們解決了跨域,但是cookie資訊并沒有送出,當然了我們也是有辦法解決的:

服務端:

網頁:

這個時候我們再通路網頁就會達到我們想要的結果了。

到這裡,我們基本上就解決了跨域的問題了,在實際應用中,我們可能還會遇到其他的問題,比如請求頭不允許跨域等,解決方法都是類似的,我們隻要添加相應的頭資訊就可以了,因為測試的原因,響應頭資訊我是直接放在了servlet中進行處理,在實際應用中,這樣做就很麻煩了,我們可以編寫一個過濾器用于跨域通路,在這裡就不貼代碼了,大家可以自己思考,對該問題進行為你善解決。