什麼是跨域請求以及實現跨域的方案

https://www.jianshu.com/p/f880878c1398

0.3 2017.10.07 18:45* 字數 3316 閱讀 15560評論 2

前言:對於跨域請求,很早以前就有去了解過,但由於一直關注的都是服務器後端開發,故也就僅僅停留在概念的理解上而沒有機會在實際開發場景中接觸獲得。最近在公司的開發任務中,須要接觸到 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攻擊:

​ 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攻擊方式後端

同源策略(Same-origin Policy)

概述

  • 同源策略是 Netscape 提出的一個著名的安全策略
  • 同源策略是瀏覽器最核心最基礎的安全策略
  • 如今全部的可支持 Javascript 的瀏覽器都會使用這個策略
  • web構建在同源策略基礎之上,瀏覽器對非同源腳本的限制措施是對同源策略的具體實現

同源策略的含義

  • DOM 層面的同源策略:限制了來自不一樣源的」Document」對象或 JS 腳本,對當前「document」對象的讀取或設置某些屬性
  • Cookie和XMLHttprequest層面的同源策略:禁止 Ajax 直接發起跨域HTTP請求(其實能夠發送請求,結果被瀏覽器攔截,不展現),同時 Ajax 請求不能攜帶與本網站不一樣源的 Cookie。
  • 同源策略的非絕對性:<script><img><iframe><link><video><audio>等帶有src屬性的標籤能夠從不一樣的域加載和執行資源。
  • 其餘插件的同源策略:flash、java applet、silverlight、googlegears等瀏覽器加載的第三方插件也有各自的同源策略,只是這些同源策略不屬於瀏覽器原生的同源策略,若是有漏洞則可能被黑客利用,從而留下XSS攻擊的後患

同源的具體含義

  • 域名、協議、端口有一個不一樣就不是同源,三者均相同,這兩個網站纔是同源

跨域解決方法

雖然在安全層面上同源限制是必要的,但有時同源策略會對咱們的合理用途形成影響,爲了不開發的應用受到限制,有多種方式能夠繞開同源策略,下面介紹的是常用的 JSONP, CORS 方法。api

JSONP

原理:
  • JSONP 是一種非官方的跨域數據交互協議
  • JSONP 本質上是利用 <script><img><iframe> 等標籤不受同源策略限制,能夠從不一樣域加載並執行資源的特性,來實現數據跨域傳輸。
  • JSONP由兩部分組成:回調函數和數據。回調函數是當響應到來時應該在頁面中調用的函數,而數據就是傳入回調函數中的JSON數據。
  • JSONP 的理念就是,與服務端約定好一個回調函數名,服務端接收到請求後,將返回一段 Javascript,在這段 Javascript 代碼中調用了約定好的回調函數,而且將數據做爲參數進行傳遞。當網頁接收到這段 Javascript 代碼後,就會執行這個回調函數,這時數據已經成功傳輸到客戶端了。
示例:

首先當前頁面中聲明有這樣的一個函數,它將做爲 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 請求

CORS

介紹

​ 跨源資源共享 Cross-Origin Resource Sharing(CORS) 是一個新的 W3C 標準,它新增的一組HTTP首部字段,容許服務端其聲明哪些源站有權限訪問哪些資源。換言之,它容許瀏覽器向聲明瞭 CORS 的跨域服務器,發出 XMLHttpReuest 請求,從而克服 Ajax 只能同源使用的限制。

​ 另外,規範也要求對於非簡單請求,瀏覽器必須首先使用 OPTION 方法發起一個預檢請求(preflight request),從而獲知服務端是否容許該跨域請求,在服務器肯定容許後,才發起實際的HTTP請求。對於簡單請求、非簡單請求以及預檢請求的詳細資料能夠閱讀HTTP訪問控制(CORS)

HTTP 協議 Header 簡析

下面對 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 跨域

與 JSONP 的比較
  • JSONP 只能實現 GET 請求,而 CORS 支持全部類型的 HTTP 請求
  • 使用 CORS ,開發者能夠是使用普通的 XMLHttpRequest 發起請求和獲取數據,比起 JSONP 有更好的錯誤處理
  • 雖然絕大多數現代的瀏覽器都已經支持 CORS,可是 CORS 的兼容性比不上 JSONP,一些比較老的瀏覽器只支持 JSONP
相關文章
相關標籤/搜索