最近在做一個項目,需要用到跨域通路,在這裡将解決問題的過程與大家分享一下。
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 >request as part of the preflight request. abnf: access-control-request-headers: “access-control-request-headers” “:” #field-name
看不懂?不着急,我們急需解決問題,上面的一大串内容的意思就是提供了這些響應頭用具解決ajax的跨域問題。當ajax進行跨域通路時,浏覽器首先會進行一次option請求判斷該請求是否被循序跨域,當響應結果為允許通路時,再進行真正的請求。
下面我們對servlet進行修改使其允許跨域:
再次打開網頁,觀察控制台輸出:

通過輸出,我們發現這時候已經可以正常通路了,這樣我們就滿足了嗎?如果有cookie之類的資訊也可以正常送出嗎?我們來繼續測試,在此更改服務端代碼:
我們先用浏覽器直接通路位址,通路兩次,觀察控制台輸出:
然後我們在通過網頁通路看看結果:
兩次結果對比,我們可以發現,雖然我們解決了跨域,但是cookie資訊并沒有送出,當然了我們也是有辦法解決的:
服務端:
網頁:
這個時候我們再通路網頁就會達到我們想要的結果了。
到這裡,我們基本上就解決了跨域的問題了,在實際應用中,我們可能還會遇到其他的問題,比如請求頭不允許跨域等,解決方法都是類似的,我們隻要添加相應的頭資訊就可以了,因為測試的原因,響應頭資訊我是直接放在了servlet中進行處理,在實際應用中,這樣做就很麻煩了,我們可以編寫一個過濾器用于跨域通路,在這裡就不貼代碼了,大家可以自己思考,對該問題進行為你善解決。