web跨域請(qǐng)求實(shí)戰(zhàn)

同源策略

理解跨域首先必須要了解同源策略扣草。同源策略是瀏覽器上為安全性考慮實(shí)施的非常重要的安全策略佳头。何謂同源:URL由協(xié)議、域名注竿、端口和路徑組成茄茁,如果兩個(gè)URL的協(xié)議魂贬、域名和端口相同,則表示他們同源裙顽。

跨域就是通過(guò)某些手段來(lái)繞過(guò)同源策略限制付燥,實(shí)現(xiàn)不同服務(wù)器之間通信的效果。具體策略限制情況可看下表:

URL 說(shuō)明
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 同一域名不同協(xié)議
http://www.a.com/a.js http://127.0.0.100/b.js 域名和域名對(duì)應(yīng)ip
http://www.a.com/a.js http://script.a.com/b.js 主域相同愈犹,子域不同
http://www.a.com/a.js http://a.com/b.js 同一域名键科,不同二級(jí)域名
http://www.a.com/a.js http://www.b.com/b.js 不同域名

只有文件協(xié)議、域名漩怎、端口和路徑全部相同勋颖,這些文件才會(huì)是同源,同源文件間請(qǐng)求無(wú)須特殊處理勋锤,當(dāng)不同源文件之間發(fā)生請(qǐng)求時(shí)饭玲,則需要跨域處理。

jsonp實(shí)現(xiàn)跨域原理

什么是jsonp叁执?
參考百度百科茄厘,JSONP(JSON with Padding)是JSON的一種“使用模式”,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問(wèn)的問(wèn)題谈宛。由于同源策略次哈,一般來(lái)說(shuō)位于 server1.example.com 的網(wǎng)頁(yè)無(wú)法與不是 server1.example.com的服務(wù)器溝通,而 HTML 的<script> 元素是一個(gè)例外入挣。利用 <script> 元素的這個(gè)開(kāi)放策略亿乳,網(wǎng)頁(yè)可以得到從其他來(lái)源動(dòng)態(tài)產(chǎn)生的 json資料,而這種使用模式就是所謂的 jsonp径筏。用 jsonp抓到的資料并不是 json葛假,而是任意的JavaScript,用 JavaScript 直譯器執(zhí)行而不是用 json解析器解析滋恬。

jsonp原理:
首先在客戶端注冊(cè)一個(gè)callback, 然后把callback的名字傳給服務(wù)器聊训。此時(shí),服務(wù)器先生成 json 數(shù)據(jù)恢氯。然后以 javascript 語(yǔ)法的方式带斑,生成一個(gè)function , function 名字就是傳遞上來(lái)的參數(shù) jsonp 。最后將 json 數(shù)據(jù)直接以入?yún)⒌姆绞窖猓胖玫?function 中勋磕,這樣就生成了一段 js 語(yǔ)法的文檔,返回給客戶端敢靡」易遥客戶端瀏覽器,解析script標(biāo)簽啸胧,并執(zhí)行返回的 javascript 文檔赶站,此時(shí)數(shù)據(jù)作為參數(shù)幔虏,傳入到了客戶端預(yù)先定義好的 callback 函數(shù)里.(動(dòng)態(tài)執(zhí)行回調(diào)函數(shù))

jsonp作用:
由于同源策略的限制,XmlHttpRequest只允許請(qǐng)求當(dāng)前源(域名贝椿、協(xié)議想括、端口)的資源,為了實(shí)現(xiàn)跨域請(qǐng)求烙博,可以通過(guò)script標(biāo)簽實(shí)現(xiàn)跨域請(qǐng)求瑟蜈,然后在服務(wù)端輸出json數(shù)據(jù)并執(zhí)行回調(diào)函數(shù),從而解決了跨域的數(shù)據(jù)請(qǐng)求习勤。

jsonp缺點(diǎn):

  1. 它只支持GET請(qǐng)求而不支持POST等其它類型的HTTP請(qǐng)求(雖然采用post+動(dòng)態(tài)生成iframe是可以達(dá)到post跨域的目的踪栋,但這樣做是一個(gè)比較極端的方式,不建議采用)图毕。
  2. jsonp易于實(shí)現(xiàn)夷都,但是也會(huì)存在一些安全隱患,如果第三方的腳本隨意地執(zhí)行予颤,那么它就可以篡改頁(yè)面內(nèi)容囤官,截獲敏感數(shù)據(jù)。但是在受信任的雙方傳遞數(shù)據(jù)蛤虐,jsonp是非常合適的選擇党饮。可以看出來(lái)jsonp跨域一般用于獲取其他域的數(shù)據(jù)驳庭。

一言不合上代碼

獲取json數(shù)據(jù)

使用jsonp獲取json數(shù)據(jù)刑顺,類似同源post請(qǐng)求獲取json數(shù)據(jù),不過(guò)jsonp只支持get請(qǐng)求饲常。

1. 客戶端注冊(cè)callback蹲堂,并將callback名字傳給服務(wù)器

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

上述代碼,callback是回傳至服務(wù)器參數(shù)贝淤,服務(wù)器使用callback參數(shù)拼接服務(wù)器端請(qǐng)求結(jié)果(json數(shù)據(jù))柒竞,返回給客戶端。

*2. 服務(wù)器端處理請(qǐng)求 *

  @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, "訂單取消成功!"));   
}

根據(jù)請(qǐng)求客戶端請(qǐng)求參數(shù)callback播聪,組裝jsonp數(shù)據(jù)朽基,返回給客戶端;

獲取html數(shù)據(jù)

有些業(yè)務(wù)場(chǎng)景需要跨域獲取其他系統(tǒng)頁(yè)面數(shù)據(jù)离陶,類似同源間get請(qǐng)求稼虎;
1. 客戶端注冊(cè)callback,并將callback名字傳給服務(wù)器

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

上述代碼招刨,callback是回傳至服務(wù)器參數(shù)霎俩,服務(wù)器使用callback參數(shù)拼接服務(wù)器端請(qǐng)求結(jié)果(json數(shù)據(jù)),返回給客戶端。
*2. 服務(wù)器端處理請(qǐng)求 *

  @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數(shù)據(jù)
}

HTTP訪問(wèn)控制

跨源資源共享(CROS)讓W(xué)eb應(yīng)用服務(wù)器能支持跨站訪問(wèn)控制茸苇,從而使得安全地進(jìn)行跨站數(shù)據(jù)傳輸成為可能。需要特別注意的是沦寂,這個(gè)規(guī)范是針對(duì)API容器的学密。比如說(shuō),要使得XMLHttpRequest在現(xiàn)代瀏覽器中可以發(fā)起跨域請(qǐng)求传藏。瀏覽器必須能支持跨源共享帶來(lái)的新的組件腻暮,包括請(qǐng)求頭和策略執(zhí)行。同樣毯侦,服務(wù)器端則需要解析這些新的請(qǐng)求頭哭靖,并按照策略返回相應(yīng)的響應(yīng)頭以及所請(qǐng)求的資源。

HTTP響應(yīng)頭

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

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

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

Access-Control-Max-Age
這個(gè)頭告訴我們這次預(yù)請(qǐng)求的結(jié)果的有效期是多久,如下:
Access-Control-Max-Age: <delta-seconds>
delta-seconds
參數(shù)表示,允許這個(gè)預(yù)請(qǐng)求的參數(shù)緩存的秒數(shù),在此期間,不用發(fā)出另一條預(yù)檢請(qǐng)求.

Access-Control-Allow-Credentials
告知客戶端,當(dāng)請(qǐng)求的credientials屬性是true的時(shí)候,響應(yīng)是否可以被得到.當(dāng)它作為預(yù)請(qǐng)求的響應(yīng)的一部分時(shí),它用來(lái)告知實(shí)際的請(qǐng)求是否使用了credentials.注意,簡(jiǎn)單的GET請(qǐng)求不會(huì)預(yù)檢,所以如果一個(gè)請(qǐng)求是為了得到一個(gè)帶有credentials的資源,而響應(yīng)里又沒(méi)有Access-Control-Allow-Credentials頭信息,那么說(shuō)明這個(gè)響應(yīng)被忽略了.
Access-Control-Allow-Credentials: true | false

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

發(fā)出預(yù)檢請(qǐng)求的例子見(jiàn)上,這個(gè)例子里就有向客戶端發(fā)送Access-Control-Allow-Methods響應(yīng)頭信息.

Access-Control-Allow-Headers
也是在響應(yīng)預(yù)檢請(qǐng)求的時(shí)候使用.用來(lái)指明在實(shí)際的請(qǐng)求中,可以使用哪些自定義HTTP請(qǐng)求頭.比如
Access-Control-Allow-Headers: X-PINGOTHER
這樣在實(shí)際的請(qǐng)求里,請(qǐng)求頭信息里就可以有這么一條:
X-PINGOTHER: pingpong
可以有多個(gè)自定義HTTP請(qǐng)求頭,用逗號(hào)分隔.
Access-Control-Allow-Headers: <field-name>[, <field-name>]*

HTTP 請(qǐng)求頭

這部分內(nèi)容列出來(lái)當(dāng)瀏覽器發(fā)出跨域請(qǐng)求時(shí)可能用到的HTTP請(qǐng)求頭.注意這些請(qǐng)求頭信息都是在請(qǐng)求服務(wù)器的時(shí)候已經(jīng)為你設(shè)置好的,當(dāng)開(kāi)發(fā)者使用跨域的XMLHttpRequest的時(shí)候,不需要手動(dòng)的設(shè)置這些頭信息.
Origin
表明發(fā)送請(qǐng)求或者預(yù)請(qǐng)求的域
Origin: <origin>

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

相關(guān)的例子可以在這里找到
Access-Control-Request-Headers
在發(fā)出預(yù)檢請(qǐng)求時(shí)帶有這個(gè)頭信息,告訴服務(wù)器在實(shí)際請(qǐng)求時(shí)會(huì)攜帶的自定義頭信息.如有多個(gè),可以用逗號(hào)分開(kāi).
Access-Control-Request-Headers: <field-name>[, <field-name>]*
尊重版權(quán)侈离,參考博客Ajax+Spring MVC實(shí)現(xiàn)跨域請(qǐng)求(JSONP),
HTTP訪問(wèn)控制

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末试幽,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子卦碾,更是在濱河造成了極大的恐慌铺坞,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件洲胖,死亡現(xiàn)場(chǎng)離奇詭異济榨,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)绿映,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門擒滑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人叉弦,你說(shuō)我怎么就攤上這事丐一。” “怎么了卸奉?”我有些...
    開(kāi)封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵钝诚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我榄棵,道長(zhǎng)凝颇,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任疹鳄,我火速辦了婚禮拧略,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘瘪弓。我一直安慰自己垫蛆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著袱饭,像睡著了一般川无。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上虑乖,一...
    開(kāi)封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天懦趋,我揣著相機(jī)與錄音,去河邊找鬼疹味。 笑死仅叫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的糙捺。 我是一名探鬼主播诫咱,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼洪灯!你這毒婦竟也來(lái)了坎缭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤婴渡,失蹤者是張志新(化名)和其女友劉穎幻锁,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體边臼,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哄尔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了柠并。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岭接。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖臼予,靈堂內(nèi)的尸體忽然破棺而出鸣戴,到底是詐尸還是另有隱情,我是刑警寧澤粘拾,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布窄锅,位于F島的核電站,受9級(jí)特大地震影響缰雇,放射性物質(zhì)發(fā)生泄漏入偷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一械哟、第九天 我趴在偏房一處隱蔽的房頂上張望疏之。 院中可真熱鬧,春花似錦暇咆、人聲如沸锋爪。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)其骄。三九已至亏镰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間拯爽,已是汗流浹背拆挥。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留某抓,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓惰瓜,卻偏偏與公主長(zhǎng)得像否副,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子崎坊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容