web跨域請求實戰

同源策略

理解跨域首先必須要了解同源策略。同源策略是瀏覽器上為安全性考慮實施的非常重要的安全策略。何謂同源:URL由協議、域名、端口和路徑組成,如果兩個URL的協議、域名和端口相同,則表示他們同源。

跨域就是通過某些手段來繞過同源策略限制,實現不同服務器之間通信的效果。具體策略限制情況可看下表:

URL 說明
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 http://www.a.com/b.js 同一域名不同端口
http://www.a.com/a.js https://www.a.com/b.js 同一域名不同協議
http://www.a.com/a.js http://127.0.0.100/b.js 域名和域名對應ip
http://www.a.com/a.js http://script.a.com/b.js 主域相同,子域不同
http://www.a.com/a.js http://a.com/b.js 同一域名,不同二級域名
http://www.a.com/a.js http://www.b.com/b.js 不同域名

只有文件協議、域名、端口和路徑全部相同,這些文件才會是同源,同源文件間請求無須特殊處理,當不同源文件之間發生請求時,則需要跨域處理。

jsonp實現跨域原理

什么是jsonp?
參考百度百科,JSONP(JSON with Padding)是JSON的一種“使用模式”,可用于解決主流瀏覽器的跨域數據訪問的問題。由于同源策略,一般來說位于 server1.example.com 的網頁無法與不是 server1.example.com的服務器溝通,而 HTML 的<script> 元素是一個例外。利用 <script> 元素的這個開放策略,網頁可以得到從其他來源動態產生的 json資料,而這種使用模式就是所謂的 jsonp。用 jsonp抓到的資料并不是 json,而是任意的JavaScript,用 JavaScript 直譯器執行而不是用 json解析器解析。

jsonp原理:
首先在客戶端注冊一個callback, 然后把callback的名字傳給服務器。此時,服務器先生成 json 數據。然后以 javascript 語法的方式,生成一個function , function 名字就是傳遞上來的參數 jsonp 。最后將 json 數據直接以入參的方式,放置到 function 中,這樣就生成了一段 js 語法的文檔,返回給客戶端。客戶端瀏覽器,解析script標簽,并執行返回的 javascript 文檔,此時數據作為參數,傳入到了客戶端預先定義好的 callback 函數里.(動態執行回調函數)

jsonp作用:
由于同源策略的限制,XmlHttpRequest只允許請求當前源(域名、協議、端口)的資源,為了實現跨域請求,可以通過script標簽實現跨域請求,然后在服務端輸出json數據并執行回調函數,從而解決了跨域的數據請求。

jsonp缺點:

  1. 它只支持GET請求而不支持POST等其它類型的HTTP請求(雖然采用post+動態生成iframe是可以達到post跨域的目的,但這樣做是一個比較極端的方式,不建議采用)。
  2. jsonp易于實現,但是也會存在一些安全隱患,如果第三方的腳本隨意地執行,那么它就可以篡改頁面內容,截獲敏感數據。但是在受信任的雙方傳遞數據,jsonp是非常合適的選擇。可以看出來jsonp跨域一般用于獲取其他域的數據。

一言不合上代碼

獲取json數據

使用jsonp獲取json數據,類似同源post請求獲取json數據,不過jsonp只支持get請求。

1. 客戶端注冊callback,并將callback名字傳給服務器

$.ajax({ url: "http://localhost:8080" + "/my/order/cancel?orderNo=" + orderNo,    
 type: "get",        
 dataType: "jsonp",    
 jsonp: 'callback',   
 jsonpCallback: 'jsonp_callback',    
 success: function (data, status) {   
    //回調處理  
    alert(data);   
   }
});

上述代碼,callback是回傳至服務器參數,服務器使用callback參數拼接服務器端請求結果(json數據),返回給客戶端。

*2. 服務器端處理請求 *

  @RequestMapping("/cancel")
  @ResponseBodypublic JSONPObject cancelOrder(String orderNo, HttpSession session, HttpServletRequest request, HttpServletResponse response, String callback) {   
   // 獲取用戶信息    
  Member member = (Member)   session.getAttribute(Constants.SESSION_MEMBER_NAME);   
  String operateIp = ClientIpUtil.getClientIp(request);    
  UserOrderOpCtx operateCtx = new UserOrderOpCtx(orderNo,String.valueOf(member.getId()),member.getNickName(),operateIp);   
   userOrderApiService.cancelOrder(operateCtx);
   response.setContentType("text/plain");    
   return new JSONPObject(callback, MapUtils.getMap(RespEnum.OK, "訂單取消成功!"));   
}

根據請求客戶端請求參數callback,組裝jsonp數據,返回給客戶端;

獲取html數據

有些業務場景需要跨域獲取其他系統頁面數據,類似同源間get請求;
1. 客戶端注冊callback,并將callback名字傳給服務器

$.ajax({ url: "http://localhost:8080" + "/my/order/cancel?orderNo=" + orderNo,    
 type: "get",        
 dataType: "jsonp",    
 jsonp: 'callback',   
 jsonpCallback: 'jsonp_callback',    
 success: function (data, status) {   
    //回調處理  
    alert(data);   
   }
});

上述代碼,callback是回傳至服務器參數,服務器使用callback參數拼接服務器端請求結果(json數據),返回給客戶端。
*2. 服務器端處理請求 *

  @RequestMapping("/cancel")
  public void cancelOrder(String orderNo, HttpSession session,HttpServletResponse response, String callback) {   
   // 獲取用戶信息    
  Member member = (Member)   session.getAttribute(Constants.SESSION_MEMBER_NAME);   
  String operateIp = ClientIpUtil.getClientIp(request);    
  UserOrderOpCtx operateCtx = new     UserOrderOpCtx(orderNo,String.valueOf(member.getId()),member.getNickName(),operateIp);   
  userOrderApiService.cancelOrder(operateCtx);
  response.setContentType("text/plain");    
  response.getWriter().write(callback+ "JSON.toJSON(MapUtils.getMap(RespEnum.OK, "訂單取消成功!"))"); //返回jsonp數據
}

HTTP訪問控制

跨源資源共享(CROS)讓Web應用服務器能支持跨站訪問控制,從而使得安全地進行跨站數據傳輸成為可能。需要特別注意的是,這個規范是針對API容器的。比如說,要使得XMLHttpRequest在現代瀏覽器中可以發起跨域請求。瀏覽器必須能支持跨源共享帶來的新的組件,包括請求頭和策略執行。同樣,服務器端則需要解析這些新的請求頭,并按照策略返回相應的響應頭以及所請求的資源。

HTTP響應頭

這部分里列出了跨域資源共享(Cross-Origin Resource Sharing)時,服務器端需要返回的響應頭信息.上一部分內容是這部分內容在實際運用中的一個概述.

Access-Control-Allow-Origin
返回的資源需要有一個 Access-Control-Allow-Origin 頭信息,語法如下:
Access-Control-Allow-Origin: <origin> | *
origin參數指定一個允許向該服務器提交請求的URI.對于一個不帶有credentials的請求,可以指定為'',表示允許來自所有域的請求.
舉個栗子,允許來自 http://mozilla.com 的請求,你可以這樣指定:
Access-Control-Allow-Origin: http://mozilla.com
如果服務器端指定了域名,而不是'
',那么響應頭的Vary值里必須包含Origin.它告訴客戶端: 響應是根據請求頭里的Origin的值來返回不同的內容的.

Access-Control-Expose-Headers
Requires Gecko 2.0(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)
設置瀏覽器允許訪問的服務器的頭信息的白名單:
Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header
這樣, X-My-Custom-Header
和 X-Another-Custom-Header這兩個頭信息,都可以被瀏覽器得到.

Access-Control-Max-Age
這個頭告訴我們這次預請求的結果的有效期是多久,如下:
Access-Control-Max-Age: <delta-seconds>
delta-seconds
參數表示,允許這個預請求的參數緩存的秒數,在此期間,不用發出另一條預檢請求.

Access-Control-Allow-Credentials
告知客戶端,當請求的credientials屬性是true的時候,響應是否可以被得到.當它作為預請求的響應的一部分時,它用來告知實際的請求是否使用了credentials.注意,簡單的GET請求不會預檢,所以如果一個請求是為了得到一個帶有credentials的資源,而響應里又沒有Access-Control-Allow-Credentials頭信息,那么說明這個響應被忽略了.
Access-Control-Allow-Credentials: true | false

帶有credential的請求在上面討論.
Access-Control-Allow-Methods
指明資源可以被請求的方式有哪些(一個或者多個). 這個響應頭信息在客戶端發出預檢請求的時候會被返回. 上面有相關的例子.
Access-Control-Allow-Methods: <method>[, <method>]*

發出預檢請求的例子見上,這個例子里就有向客戶端發送Access-Control-Allow-Methods響應頭信息.

Access-Control-Allow-Headers
也是在響應預檢請求的時候使用.用來指明在實際的請求中,可以使用哪些自定義HTTP請求頭.比如
Access-Control-Allow-Headers: X-PINGOTHER
這樣在實際的請求里,請求頭信息里就可以有這么一條:
X-PINGOTHER: pingpong
可以有多個自定義HTTP請求頭,用逗號分隔.
Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP 請求頭

這部分內容列出來當瀏覽器發出跨域請求時可能用到的HTTP請求頭.注意這些請求頭信息都是在請求服務器的時候已經為你設置好的,當開發者使用跨域的XMLHttpRequest的時候,不需要手動的設置這些頭信息.
Origin
表明發送請求或者預請求的域
Origin: <origin>

參數origin是一個URI,告訴服務器端,請求來自哪里.它不包含任何路徑信息,只是服務器名.
Note: Origin的值可以是一個空字符串,這是很有用的.
注意,不僅僅是跨域請求,普通請求也會帶有ORIGIN頭信息.
Access-Control-Request-Method
在發出預檢請求時帶有這個頭信息,告訴服務器在實際請求時會使用的請求方式
Access-Control-Request-Method: <method>

相關的例子可以在這里找到
Access-Control-Request-Headers
在發出預檢請求時帶有這個頭信息,告訴服務器在實際請求時會攜帶的自定義頭信息.如有多個,可以用逗號分開.
Access-Control-Request-Headers: <field-name>[, <field-name>]*
尊重版權,參考博客Ajax+Spring MVC實現跨域請求(JSONP),
HTTP訪問控制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,698評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,202評論 3 426
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,742評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,580評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,297評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,688評論 1 327
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,693評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,875評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,438評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,183評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,384評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,931評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,612評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,022評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,297評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,093評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,330評論 2 377

推薦閱讀更多精彩內容