1. 程式人生 > >ajax跨域解決辦法

ajax跨域解決辦法

跨域的安全限制都是指瀏覽器端來說的.伺服器端是不存在跨域安全限制的,

什麼是跨域:

跨域是指從一個域名的網頁去請求另一個域名的資源。比如從www.baidu.com 頁面去請求 www.google.com 的資源。跨域的嚴格一點的定義是:只要 協議,域名,埠有任何一個的不同,就被當作是跨域

為什麼瀏覽器要限制跨域訪問呢?

原因就是安全問題:如果一個網頁可以隨意地訪問另外一個網站的資源,那麼就有可能在客戶完全不知情的情況下出現安全問題。比如下面的操作就有安全問題:

  1. 使用者訪問www.mybank.com ,登陸並進行網銀操作,這時cookie啥的都生成並存放在瀏覽器
  2. 使用者突然想起件事,並迷迷糊糊地訪問了一個邪惡的網站 www.xiee.com
  3. 這時該網站就可以在它的頁面中,拿到銀行的cookie,比如使用者名稱,登陸token等,然後發起對www.mybank.com 的操作。
  4. 如果這時瀏覽器不予限制,並且銀行也沒有做響應的安全處理的話,那麼使用者的資訊有可能就這麼洩露了。

為什麼要跨域

既然有安全問題,那為什麼又要跨域呢?有時公司內部有多個不同的子域,比如一個是location.company.com ,而應用是放在app.company.com , 這時想從 app.company.com去訪問 location.company.com 的資源就屬於跨域。

跨域訪問需要的兩件寶貝

由於瀏覽器一般不對script,img等進行跨域限制,所以我們有機會通過script的方式來實現跨域訪問。

跨域訪問需要用到兩樣東東,一個是JSON,一種基於文字的傳輸協議;一種是JSONP,一群碼農想出來的跨域解決方案。關於JSON與JSONP的解釋,可以參考 JSON & JSONP

了便於客戶端使用資料,逐漸形成了一種非正式傳輸協議,人們把它稱作JSONP,該協議的一個要點就是允許使用者傳遞一個callback引數給服務端,然後服務端返回資料時會將這個callback引數作為函式名來包裹住JSON資料,這樣客戶端就可以隨意定製自己的函式來自動處理返回資料了。

由 於此前很少寫前端的程式碼(哈哈,不合格的程式設計師啊),最近專案中用到json作為系統間互動的手段,自然就伴隨著眾多ajax請求,隨之而來的就是要解決 ajax的跨域問題。本篇將講述一個小白從遇到跨域不知道是跨域問題,到知道是跨域問題不知道如何解決,再到解決跨域問題,最後找到兩種方法解決ajax 跨域問題的全過程。

不知是跨域問題

起 因是這樣的,為了複用,減少重複開發,單獨開發了一個使用者許可權管理系統,共其他系統獲取認證與授權資訊,暫且稱之為A系統;呼叫A系統以B為例。在B系統 中用ajax呼叫A系統系統的介面(資料格式為json),當時特別困惑,在A系統中訪問相應的url可正常回返json資料,但是在B系統中使用 ajax請求同樣的url則一點兒反應都沒有,好像什麼都沒有發生一樣。這樣反反覆覆改來改去好久都沒能解決,於是求救同事,提醒可能是ajax跨域問 題,於是就將這個問題當做跨域問題來解決了。

知跨域而不知如何解決

知道問題的確切原因,剩下的就是找到解決問題的方法了。google了好久,再次在同事的指點下知道jQuery的ajax有jsonp這樣的屬性可以用來解決跨域的問題。

1.第一種解決方式

總結jsonp步驟:

step1:頁面

js請求中url請求末尾加callback=?引數。使用 JSONP 形式呼叫函式時,如 "myurl?callback=?" jQuery 將自動替換 ? 為正確的函式名,以執行回撥函式。

js返回引數dataType:標明為jsonp

step2:伺服器修改

拼舊的返回資料,在外層加callback字串

這樣應該就ok了!

現在也知道了怎樣來解決跨域問題,餘下的就是實現的細節了。實現的過程中錯誤還是避免不了的。由於不瞭解json和jsonp兩種格式的區別,也犯了錯誤,google了好久才解決。

首先來看看在頁面中如何使用jQuery的ajax解決跨域問題的簡單版:

$(document).ready(function(){
   var url='http://localhost:8080/WorkGroupManagment/open/getGroupById"
       +"?id=1&callback=?';
   $.ajax({
     url:url,
     dataType:'jsonp',
     processData: false, 
     type:'get',
     success:function(data){
       alert(data.name);
     },
     error:function(XMLHttpRequest, textStatus, errorThrown) {
       alert(XMLHttpRequest.status);
       alert(XMLHttpRequest.readyState);
       alert(textStatus);
     }});
   });

這樣寫是完全沒有問題的,起先error的處理函式中僅僅是alert(“error”),為了進一步弄清楚是什麼原因造成了錯誤,故將處理函式變 為上面的實現方式。最後一行alert使用為;parsererror。百思不得其解,繼續google,最終還是在萬能的stackoverflow找 到了答案,連結在這裡。原因是jsonp的格式與json格式有著細微的差別,所以在server端的程式碼上稍稍有所不同。

比較一下json與jsonp格式的區別:

json格式:

{
    "message":"獲取成功",
    "state":"1",
    "result":{"name":"工作組1","id":1,"description":"11"}
}

jsonp格式:

callback({
    "message":"獲取成功",
    "state":"1",
    "result":{"name":"工作組1","id":1,"description":"11"}
})

看出來區別了吧,在url中callback傳到後臺的引數是神馬callback就是神馬,

jsonp比json外面有多了一層,callback()。

這樣就知道怎麼處理它了。於是修改後臺程式碼。

後臺java程式碼最終如下:

@RequestMapping(value = "/getGroupById")
  public String getGroupById(@RequestParam("id") Long id,
      HttpServletRequest request, HttpServletResponse response)
      throws IOException {
    String callback = request.getParameter("callback");
    ReturnObject result = null;
    Group group = null;
    try {
      group = groupService.getGroupById(id);
      result = new ReturnObject(group, "獲取成功", Constants.RESULT_SUCCESS);
    } catch (BusinessException e) {
      e.printStackTrace();
      result = new ReturnObject(group, "獲取失敗", Constants.RESULT_FAILED);
    }
    String json = JsonConverter.bean2Json(result);
    response.setContentType("text/html");
    response.setCharacterEncoding("utf-8");
    PrintWriter out = response.getWriter();
    out.print(callback + "(" + json + ")");
    return null;
  }

注意這裡需要先將查詢結果轉換我json格式,然後用引數callback在json外面再套一層,就變成了jsonp

指定資料型別為jsonp的ajax就可以做進一步處理了。

雖然這樣解決了跨域問題,還是回顧下造成parsererror的原因。原因在於盲目的把json格式的資料當做jsonp格式的資料讓ajax處理,造成了這個錯誤,此時server端程式碼是這樣的:

@RequestMapping(value = "/getGroupById")
  @ResponseBody
  public ReturnObject getGroupById(@RequestParam("id") Long id,
      HttpServletRequest request, HttpServletResponse response){
    String callback = request.getParameter("callback");
    ReturnObject result = null;
    Group group = null;
    try {
      group = groupService.getGroupById(id);
      result = new ReturnObject(group, "獲取成功", Constants.RESULT_SUCCESS);
    } catch (BusinessException e) {
      e.printStackTrace();
      result = new ReturnObject(group, "獲取失敗", Constants.RESULT_FAILED);
    }
    return result;
  }

至此解決ajax跨域問題的第一種方式就告一段落。

2.第二種解決方式【通過設定Access-Control-Allow-Origin來實現跨域訪問比較簡單。】
修改controller在被請求的Response header中加入
在程式中新增


response.addHeader("Access-Control-Allow-Origin","*");//表示允許任何域名跨域訪問
如果需要指定某域名才允許跨域訪問,
只需把Access-Control-Allow-Origin:*改為Access-Control-Allow-Origin:允許的域名     例如:
response.addHeader('Access-Control-Allow-Origin:http://www.client.com');
 

@RequestMapping("findCourseNoteByKeywords")
	@ResponseBody
	public ApiResult findCourseNoteByKeywords(String keyWord,HttpServletResponse response){
		response.addHeader("Access-Control-Allow-Origin","*");
		ApiResult apiResult = new ApiResult();
		List<Map<String, Object>> result = bCourseNoteService.findCourseNotesByKeywords(keyWord);
		Map map  = new HashMap<String, List<Map<String, Object>> >();
		map.put("data", result);
		apiResult.setData(map);
		return apiResult;
		
	}

3.追加一種解決方式

追求永無止境,在google的過程中,無意中發現了一個

專門用來解決跨域問題的jQuery外掛-jquery-jsonp

有第一種方式的基礎,使用jsonp外掛也就比較簡單了,server端程式碼無需任何改動。

來看一下如何使用jquery-jsonp外掛解決跨域問題吧。

var url="http://localhost:8080/WorkGroupManagment/open/getGroupById"
    +"?id=1&callback=?";
$.jsonp({
  "url": url,
  "success": function(data) {
    $("#current-group").text("當前工作組:"+data.result.name);
  },
  "error": function(d,msg) {
    alert("Could not find user "+msg);
  }
});

SONP方法是一種非官方方法,而且這種方法只支援GET方式,不如POST方式安全。

即使使用jQuery的jsonp方法,type設為POST,也會自動變為GET。

三種方案比較;

方法1需要改動:頁面+伺服器端程式碼

方法2需要改動:伺服器端程式碼

方法3需要改動:頁面(不過需要引入jquery的外掛)

這裡針對ajax與jsonp的異同再做一些補充說明:

1、ajax和jsonp這兩種技術在呼叫方式上“看起來”很像,目的也一樣,都是請求一個url,然後把伺服器返回的資料進行處理,因此jquery和ext等框架都把jsonp作為ajax的一種形式進行了封裝;

2、但ajax和jsonp其實本質上是不同的東西。ajax的核心是通過XmlHttpRequest獲取非本頁內容,而jsonp的核心則是動態新增<script>標籤來呼叫伺服器提供的js指令碼。

3、所以說,其實ajax與jsonp的區別不在於是否跨域,ajax通過服務端代理一樣可以實現跨域,jsonp本身也不排斥同域的資料的獲取。

4、還有就是,jsonp是一種方式或者說非強制性協議,如同ajax一樣,它也不一定非要用json格式來傳遞資料,如果你願意,字串都行,只不過這樣不利於用jsonp提供公開服務。

總而言之,jsonp不是ajax的一個特例,哪怕jquery等巨頭把jsonp封裝進了ajax,也不能改變著一點!