前言:對於跨域請求,很早以前就有去了解過,但由於一直關注的都是服務器後端開發,故也就僅僅停留在概念的理解上而沒有機會在實際開發場景中接觸獲得。最近在公司的開發任務中,須要接觸到 Ajax 跨域請求,因爲以前沒有遇到過相似的問題,在開發過程當中遇到很多困難,也查閱了很多資料和博客。在這過程當中收穫了很多,故特地寫下如下文章總結,若是文章有什麼不足之處,還望各位指出。javascript
在 HTML 中,<a>
, <form>
, <img>
, <script>
, <iframe>
, <link>
等標籤以及 Ajax 均可以指向一個資源地址,而所謂的跨域請求就是指:當前發起請求的域與該請求指向的資源所在的域不同。這裏的域指的是這樣的一個概念:咱們認爲若協議 + 域名 + 端口號均相同,那麼就是同域。php
舉個例子:假如一個域名爲aaa.cn
的網站,它發起一個資源路徑爲aaa.cn/books/getBookInfo
的 Ajax 請求,那麼這個請求是同域的,由於資源路徑的協議、域名以及端口號與當前域一致(例子中協議名默認爲http,端口號默認爲80)。可是,若是發起一個資源路徑爲bbb.com/pay/purchase
的 Ajax 請求,那麼這個請求就是跨域請求,由於域不一致,與此同時因爲安全問題,這種請求會受到同源策略限制。html
一般,瀏覽器會對上面提到的跨域請求做出限制。瀏覽器之因此要對跨域請求做出限制,是出於安全方面的考慮,由於跨域請求有可能被不法分子利用來發動 CSRF攻擊。java
CSRF(Cross-site request forgery),中文名稱:跨站請求僞造,也被稱爲:one click attack/session riding,縮寫爲:CSRF/XSRF。CSRF攻擊者在用戶已經登陸目標網站以後,誘使用戶訪問一個攻擊頁面,利用目標網站對用戶的信任,以用戶身份在攻擊頁面對目標網站發起僞造用戶操做的請求,達到攻擊目的。web
CSRF 攻擊的原理大體描述以下:有兩個網站,其中A網站是真實受信任的網站,而B網站是危險網站。在用戶登錄了受信任的A網站是,本地會存儲A網站相關的Cookie,而且瀏覽器也維護這一個Session會話。這時,若是用戶在沒有登出A網站的狀況下訪問危險網站B,那麼危險網站B就能夠模擬發出一個對A網站的請求(跨域請求)對A網站進行操做,而在A網站的角度來看是並不知道請求是由B網站發出來的(Session和Cookie均爲A網站的),這時便成功發動一次CSRF 攻擊。ajax
於是 CSRF 攻擊能夠簡單理解爲:攻擊者盜用了你的身份,以你的名義發送而已請求。CSRF可以作的事情包括:以你名義發送郵件,發消息,盜取你的帳號,甚至於購買商品,虛擬貨幣轉帳......形成的問題包括:我的隱私泄露以及財產安全。json
所以,大多數瀏覽器都會跨域請求做出限制,這是從瀏覽器層面上的對 CSRF 攻擊的一種防護,可是須要注意的是在複雜的網絡環境中藉助瀏覽器來防護 CSRF 攻擊並不足夠,還須要從服務端或者客戶端方面入手防護。詳細能夠參考這篇文章淺談CSRF攻擊方式後端
<script><img><iframe><link><video><audio>
等帶有src屬性的標籤能夠從不一樣的域加載和執行資源。flash、java applet、silverlight、googlegears
等瀏覽器加載的第三方插件也有各自的同源策略,只是這些同源策略不屬於瀏覽器原生的同源策略,若是有漏洞則可能被黑客利用,從而留下XSS攻擊的後患雖然在安全層面上同源限制是必要的,但有時同源策略會對咱們的合理用途形成影響,爲了不開發的應用受到限制,有多種方式能夠繞開同源策略,下面介紹的是常用的 JSONP, CORS 方法。api
<script><img><iframe>
等標籤不受同源策略限制,能夠從不一樣域加載並執行資源的特性,來實現數據跨域傳輸。首先當前頁面中聲明有這樣的一個函數,它將做爲 JSONP 的回調函數處理做爲函數參數傳入的數據跨域
<script type="text/javascript"> function dosomething(jsondata){ //處理得到的json數據 } </script>
而後,咱們就能夠藉助 <script><img><iframe>
等標籤能夠引入不一樣域資源的特性,將須要發送的請求的路徑做爲src參數,其中須要注意的是:須要告知服務端回調函數的函數名。
<script src="http://example.com/data.php?callback=dosomething"></script>
這時服務端在返回數據的時候,就會返回一端 Javascript 代碼,在 Javascript代碼中調用了回調函數,而且須要返回的數據做爲回調函數的參數
dosomething(['a','b','c']);
最後頁面成功加載了剛纔指定路徑的資源後,將會執行該 Javascript 代碼,dosomething
函數將執行,這時一次跨域請求完成。
另外,若是頁面引入了 jQuery,那麼能夠經過它封裝的方法很方便的實現JSONP操做了
// Using YQL and JSONP $.ajax({ url: "http://query.yahooapis.com/v1/public/yql", // The name of the callback parameter, as specified by the YQL service jsonp: "callback", // Tell jQuery we're expecting JSONP dataType: "jsonp", // Tell YQL what we want and that we want JSON data: { q: "select title,abstract,url from search.news where query=\"cat\"", format: "json" }, // Work with the response success: function( response ) { console.log( response ); // server response } });
JSONP 的優勢是:它不像XMLHttpRequest
對象實現的Ajax請求那樣受到同源策略的限制;它的兼容性更好,在更加古老的瀏覽器中均可以運行。
JSONP 的缺點是:它只支持 GET 請求,而不支持 POST 請求等其餘類型的 HTTP 請求
跨源資源共享 Cross-Origin Resource Sharing(CORS) 是一個新的 W3C 標準,它新增的一組HTTP首部字段,容許服務端其聲明哪些源站有權限訪問哪些資源。換言之,它容許瀏覽器向聲明瞭 CORS 的跨域服務器,發出 XMLHttpReuest 請求,從而克服 Ajax 只能同源使用的限制。
另外,規範也要求對於非簡單請求,瀏覽器必須首先使用 OPTION 方法發起一個預檢請求(preflight request),從而獲知服務端是否容許該跨域請求,在服務器肯定容許後,才發起實際的HTTP請求。對於簡單請求、非簡單請求以及預檢請求的詳細資料能夠閱讀HTTP訪問控制(CORS) 。
下面對 CORS 中新增的 HTTP 首部字段進行簡析:
Access-Control-Allow-Origin
響應首部中能夠攜帶這個頭部表示服務器容許哪些域能夠訪問該資源,其語法以下:
Access-Control-Allow-Origin: <origin> | *
其中,origin 參數的值指定了容許訪問該資源的外域 URI。對於不須要攜帶身份憑證的請求,服務器能夠指定該字段的值爲通配符,表示容許來自全部域的請求。
Access-Control-Allow-Methods
該首部字段用於預檢請求的響應,指明實際請求所容許使用的HTTP方法。其語法以下:
Access-Control-Allow-Methods: <method>[, <method>]*
Access-Control-Allow-Headers
該首部字段用於預檢請求的響應。指明瞭實際請求中容許攜帶的首部字段。其語法以下:
Access-Control-Allow-Headers: <field-name>[, <field-name>]*
Access-Control-Max-Age
該首部字段用於預檢請求的響應,指定了預檢請求可以被緩存多久,其語法以下:
Access-Control-Max-Age: <delta-seconds>
Access-Control-Allow-Credentials
該字段可選。它的值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true
,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器。其語法以下:
Access-Control-Allow-Credentials: true
另外,若是要把 Cookie 發送到服務器,除了服務端要帶上Access-Control-Allow-Credentials
首部字段外,另外一方面請求中也要帶上withCredentials
屬性。
可是須要注意的是:若是須要在 Ajax 中設置和獲取 Cookie,那麼Access-Control-Allow-Origin
首部字段不能設置爲*
,必須設置爲具體的 origin 源站。詳細可閱讀文章CORS 跨域 Cookie 的設置與獲取
Origin
該首部字段代表預檢請求或實際請求的源站。不論是否爲跨域請求,Origin字段老是被髮送。其語法以下:
Origin: <origin>
Access-Control-Request-Method
該首部字段用於預檢請求。其做用是,將實際請求所使用的 HTTP 方法告訴服務器。其語法以下:
Access-Control-Request-Method: <method>
Access-Control-Request-Headers
該首部字段用於預檢請求。其做用是,將實際請求所攜帶的首部字段告訴服務器。其語法以下:
Access-Control-Request-Headers: <field-name>[, <field-name>]*
假設咱們在 bbb.cn 域名下,發送一個 Ajax 請求到 aaa.cn 域名,其路徑以下:http://aaa.cn/localserver/api/corsTest
。因爲同源策略,這樣的 Ajax 請求將會被瀏覽器所攔截,獲得下面的信息:
若想可以發送跨域請求,咱們只須要在服務器的響應中配置適當的CORS HTTP 首部字段就能夠了,例如能夠加入如下的首部字段:
Access-Control-Allow-Methods:*
此時,Ajax請求就能夠順利的發送和接收了,對應的請求和響應頭部以下:
對於在 Java Web 項目中,如何在 Servlet 或這 Spring MVC 中配置 CORS 能夠閱讀文章Spring MVC 實現 CORS 跨域 。