天天看點

跨域解決方案(一)

工作中我們經常會遇到ajax跨域的問題,今天我們了解一下跨域的原因和各種解決方案

跨域原因

<a href="http://s4.51cto.com/wyfs02/M00/8C/23/wKioL1hjbgWiI8UAAABaLoGOrro496.png" target="_blank"></a>

所謂跨域就是:在a.com域下,通過ajax請求通路b.com域下的資源,出于安全的考慮,浏覽器同源政策允許跨域寫,而不允許跨域讀,寫就是上行,發送請求,send request,讀就是下行,接受響應,receive response;

通俗點就是:a域向b域發送ajax請求,可以請求成功,但在請求的結果傳回時,浏覽器會先去判斷a域與b域是否是同一個域下,“是”則傳回資料,“否”則攔截資料并抛出如下異常資訊

No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.

解決方案

主流解決方案有

jsonp

背景代理

cors

其他解決方案有

tomcat設定過濾器(基于cors)

nginx設定(基于cors)

chrome通過指令屏蔽跨域安全檢查

chorme通過插件實作跨域(基于cors)

JSONP

原理:

浏覽器的同源政策中雖然限制了不同域之間無法通過ajax請求得到響應結果,但有些html标簽可以突破該限制,如img,srcipt,iframe标簽中的src屬性,可以引用不屬于本域的資源且執行成功,jsonp的原理就是利用src屬性的特性,動态的建立這些标簽發送請求,接收響應後執行回調

案例:在a域中向b域發送ajax請求

      a域:前端js

<code>       </code><code>//定義回調函數    </code>

<code>       </code><code>function myCallBack(data){   </code>

<code>            </code><code>alert(</code><code>"回調的資料為:"</code><code>+data);</code>

<code>       </code><code>} </code>

<code>     </code> 

<code>      </code><code>//按鈕點選事件</code>

<code>      </code><code>function btnClick(){  </code>

<code>          </code><code>//建立script标簽  </code>

<code>          </code><code>var script = document.createElement(</code><code>'script'</code><code>);   </code>

<code>          </code><code>//b域的資源路徑   </code>

<code>          </code><code>script.src = </code><code>'http://www.b.com/getData?callback=myCallBack'</code><code>;   </code>

<code>          </code><code>// 在文檔中追加script标簽,開始發送請求到背景,響應結果後會自動調用myCallBack   </code>

<code>          </code><code>document.body.appendChild(script);</code>

<code>      </code><code>}</code>

 a域:html

<code> </code><code>&lt;</code><code>input</code> <code>type</code><code>=</code><code>"button"</code> <code>onclick</code><code>=</code><code>"btnClick"</code> <code>value</code><code>=</code><code>"請求資源"</code><code>/&gt;</code>

b域:java後端代碼 

<code>    </code><code>//映射請求路徑    </code>

<code>    </code><code>@RequestMapping("/getData")    </code>

<code>    </code><code>@ResponseBody    </code>

<code>    </code><code>public String getData(String callback){        </code>

<code>        </code><code>//擷取回調函數名後,拼接函數加資料傳回        </code>

<code>        </code><code>return callback+"('通路成功')";   </code>

<code>    </code><code>}</code>

流程:

1.a域前端定義回調函數和點選事件

2.當a域使用者點選按鈕後,觸發點選事件,建立script對象且向b域後端發送請求

3.當b域後端接收回調函數名後,拼接結果傳回

4.執行a域前端中的回調函數

優點:

1. 環境依賴少,隻需要浏覽器相容js就可以執行

2.相容性強,基本所有的浏覽器都可以采用該方案(包括IE6,7等老式浏覽器)

缺點:

1.隻能執行get類型http請求,非get請求無法采用該方案

2.需要b域後端代碼配合,侵入性強,在b域後端代碼不可控的情況下無法采用

其他:

jquery中的jsonp原理與上面案例類似,隻是在發送請求的過程中會幫我們動态建立回調函數,在調用完成後删除回調函數減少記憶體消耗,當然也可以通過設定指定回調函數名稱傳給背景

原理:http請求可以正常通路跨域的資源,如果a域想通路b域的資源,可以在a域中的前端發送ajax請求到a域的後端,在a域的後端中發送http請求到b域擷取資源,然後将資源傳回給a域前端

案例:

a域:html

<code>a域:前端js</code>

<code>    </code><code>&lt;</code><code>script</code> <code>src</code><code>=</code><code>"/javascripts/jquery.js"</code> <code>type</code><code>=</code><code>"text/javascript"</code><code>&gt;&lt;/</code><code>script</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>script</code> <code>type</code><code>=</code><code>"text/javascript"</code><code>&gt;</code>

<code> </code> 

<code>        </code><code>//按鈕點選事件</code>

<code>        </code><code>function btnClick(){</code>

<code>            </code><code>//發送ajax請求到a域自己的後端</code>

<code>            </code><code>$.post("http://www.a.com/getData",function(data){</code>

<code>                </code><code>alert(data);</code>

<code>            </code><code>});</code>

<code>        </code><code>}</code>

<code>    </code><code>&lt;/</code><code>script</code><code>&gt;</code>

<code></code>a域:後端代碼(java)

<code> </code><code>//映射請求路徑</code>

<code>    </code><code>@RequestMapping("/getData")</code>

<code>    </code><code>@ResponseBody</code>

<code>    </code><code>public String getData(){</code>

<code>        </code><code>CloseableHttpClient httpClient= null;</code>

<code>        </code><code>String result=null;</code>

<code>        </code><code>try {</code>

<code>                </code><code>httpClient= HttpClients.createDefault();</code>

<code>                </code><code>HttpGet getMethod=new HttpGet(new URI("http://www.b.com/getData"));</code>

<code>                </code><code>//請求b域資源 得到響應對象</code>

<code>                </code><code>HttpResponse response= httpClient.execute(getMethod);</code>

<code>                </code><code>result= EntityUtils.toString(response.getEntity(), "utf-8");</code>

<code>        </code><code>} catch (IOException e) {</code>

<code>            </code><code>e.printStackTrace();</code>

<code>        </code><code>}catch (Exception e){</code>

<code>        </code><code>}finally {</code>

<code>            </code><code>if(httpClient!=null){</code>

<code>                </code><code>try {</code>

<code>                    </code><code>httpClient.close();</code>

<code>                </code><code>} catch (IOException e) {</code>

<code>                    </code><code>e.printStackTrace();</code>

<code>                </code><code>}</code>

<code>            </code><code>}</code>

<code>            </code><code>//将b域資源傳回給a域</code>

<code>            </code><code>return result;</code>

 b域:後端代碼(java)

<code>    </code><code>//映射請求路徑</code>

<code>        </code><code>/**</code>

<code>         </code><code>* 省略業務邏輯代碼</code>

<code>         </code><code>*/</code>

<code>       </code><code>return "success";</code>

流程

1.使用者在a域點選按鈕,觸發點選事件,向a域後端發送ajax請求

2.a域後端發送http請求到b域後端請求資源

3.請求b域資源成功後将結果傳回a域前端,a域前端輸出資源

1.可以使用get,post等多種請求方式

2.b域後端不用配合a域做傳回結果的修改

缺點:

1.性能較低,a域每一次請求b域資源,本質會發送2次請求,1次是發送ajax請求到a域自己後端,1次是a域後端發送請求到b域

2.代碼複雜,不利于維護。當a域向b域請求資源比較多的情況下,每一個請求都要寫一個後端代理,在代碼量和維護上都是一個考驗,該方案隻适合跨域請求資源少,且b域不可控(即無法控制b域後端拼接結果傳回)的情況下使用

本文轉自 興趣e族 51CTO部落格,原文連結:http://blog.51cto.com/simplelife/1886821