1. 程式人生 > >通過cors解決跨域

通過cors解決跨域

1.跨域概念

      因為跨域問題是瀏覽器對於ajax請求的一種安全限制:一個頁面發起的ajax請求,只能是於當前頁同域名的路徑,這能有效的阻止跨站攻擊。因此:跨域問題 是針對ajax的一種限制。

【解決跨域問題的方法】

 1.1、Jsonp最早的解決方案,利用script標籤可以跨域的原理實現。
  限制:
  (1)需要服務的支援;
  (2)只能發起GET請求;

1.2、nginx反向代理

    思路是:利用nginx反向代理把跨域為不跨域,支援各種請求方式;
    缺點:需要在nginx進行額外配置,語義不清晰。

1.3、CORS  規範化的跨域請求解決方案,安全可靠。
  優勢:
  -(1)在服務端進行控制是否允許跨域,可自定義規則;
  (2)支援各種請求方式;
  缺點: 會產生額外的請求

2.cors概念

     CORS是一個W3C標準,全稱是"跨域資源共享"(Cross-origin resource sharing)。它允許瀏覽器向跨源伺服器,發出XMLHttpRequest請求,從而克服了AJAX只能同源使用的限制。CORS需要瀏覽器和伺服器同時支援。目前,所有瀏覽器都支援該功能,IE瀏覽器不能低於IE10


(1)瀏覽器端:  目前,所有瀏覽器都支援該功能(IE10以下不行)。整個CORS通訊過程,都是瀏覽器自動完成,不需要使用者參與。
(2) 服務端:  CORS通訊與AJAX沒有任何差別,因此你不需要改變以前的業務邏輯。只不過,瀏覽器會在請求中攜帶一些頭資訊,我們需要以此判斷是否執行其跨域,然後在響應頭中加入一些資訊即可。這一般通過過濾器完成即可。

2.1、簡單請求

只要同時滿足以下兩大條件,就屬於簡單請求。

(1) 請求方法是以下三種方法之一:

  • HEAD
  • GET
  • POST

(2)HTTP的頭資訊不超出以下幾種欄位:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

當瀏覽器發現發現的ajax請求是簡單請求時,會在請求頭中攜帶一個欄位:Origin.

     Origin中會指出當前請求屬於哪個域(協議+域名+埠)。服務會根據這個值決定是否允許其跨域。如果伺服器允許跨域,需要在返回的響應頭中攜帶下面資訊:

Access-Control-Allow-Origin: http://manage.wzy.com
Access-Control-Allow-Credentials: true
Content-Type: text/html; charset=utf-8
  • ccess-Control-Allow-Origin:可接受的域,是一個具體域名或者*,代表任意;

  • Access-Control-Allow-Credentials:是否允許攜帶cookie,預設情況下,cors不會攜帶cookie,除非這個值是true。

注意:如果跨域請求要想操作cookie,需要滿足3個條件:
(1)服務的響應頭中需要攜帶Access-Control-Allow-Credentials並且為true;
(2)瀏覽器發起ajax需要指定withCredentials 為true;
  (3)響應頭中的Access-Control-Allow-Origin一定不能為*,必須是指定的域名。

2.2、特殊請求

      不符合簡單請求的條件,會被瀏覽器判定為特殊請求,,例如請求方式為PUT。特殊請求會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。瀏覽器先詢問伺服器,當前網頁所在的域名是否在伺服器的許可名單之中,以及可以使用哪些HTTP動詞和頭資訊欄位。只有得到肯定答覆,瀏覽器才會發出正式的XMLHttpRequest請求,否則就報錯。
一個“預檢”請求的樣板:

OPTIONS /cors HTTP/1.1
Origin: http://manage.leyou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.leyou.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

與簡單請求相比,除了Origin以外,多了兩個頭:

  • Access-Control-Request-Method:接下來會用到的請求方式,比如PUT;

  • Access-Control-Request-Headers:會額外用到的頭資訊。

服務的收到預檢請求,如果許可跨域,會發出響應(也就是預請求的響應):  

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://manage.wzy.com
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Max-Age: 1728000
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain

 除了Access-Control-Allow-OriginAccess-Control-Allow-Credentials以外,這裡又額外多出3個頭:

  • Access-Control-Allow-Methods:允許訪問的方式;

  • Access-Control-Allow-Headers:允許攜帶的頭;

  • Access-Control-Max-Age:本次許可的有效時長,單位是秒,過期之前的ajax請求就無需再次進行預檢。

3.springboot配置cors配置類

 

mport org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1.新增CORS配置資訊
        CorsConfiguration config = new CorsConfiguration();
        //1) 允許的域,不要寫*,否則cookie就無法使用了
        config.addAllowedOrigin("http://manage.wzy.com");
        //2) 是否傳送Cookie資訊
        config.setAllowCredentials(true);
        //3) 允許的請求方式
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        // 4)允許的頭資訊
        config.addAllowedHeader("*");

        //2.新增對映路徑,我們攔截一切請求
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

        //3.返回新的CorsFilter.
        return new CorsFilter(configSource);
    }
}