springboot系列文章之實現跨域請求(CORS)

原文博客:pjmike的博客html

CORS介紹

跨域資源共享向來都是熱門的需求,咱們可使用 CORS 來快速實現 跨域訪問,只須要在服務端進行受權便可,無需在前端添加額外的設置前端

簡單說,CORS是一種訪問機制,英文全稱: Cross-Origin Resource Sharing,即咱們說的跨域資源共享。當一個資源從與該資源自己所在服務器不一樣的域或端口請求一個資源時,資源會發起一個跨域HTTP請求。好比,在一個域名下的網頁中,調用另外一個域名中的資源。java

CORS的工做原理

CORS 實現跨域訪問並非一蹴而就的,須要藉助瀏覽器的支持,從原理題圖咱們能夠看到,簡單的請求(一般指 GET/POST/HEAD 方式,並無去增長額外的請求頭信息) 直接建立了跨域請求的 XMLHttpRequest對象,而非簡單請求(那種對服務器有特殊要求的請求,好比請求方法是 PUTDELETE,或者 Content-Type字段的類型是 application/json) 則要求先發送一個 "預檢" 請求,待服務器批准後才能真正發起跨域訪問請求。 git

cors

簡單請求

下面分析摘自 阮一峯的 跨域資源共享 CORS 詳解github

對於簡單請求 (GET/POST/HEAD,瀏覽器直接發出 CORS請求,具體來講,就是在頭信息之中,增長一個 Origin 字段。以下圖所示:spring

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
複製代碼

上面的 Origin 字段用來講明,本次請求來自哪一個源(協議+域名+端口)。服務器根據這個值,決定是否贊成此次請求。json

若是 Origin 指定的源,不在許可範圍內,服務器會返回一個正常的 HTTP響應。瀏覽器發現,這個迴應的頭信息沒有包含 Access-Control-Allow-Origin 字段,就知道錯了,從而拋出一個錯誤,被 XMLHttpRequestonerror回調函數捕獲。後端

若是 Origin指定的域名在許可範圍內,服務器返回的響應,會多出幾個頭信息字段api

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
複製代碼

下面總結下 簡單請求 與 CORS 有關的請求頭與響應頭跨域

Request Headers

  • Origin :表示跨域請求的原始域

Response Headers

  • Access-Control-Allow-Origin : 表示容許哪些原始域進行跨域訪問,它的值要麼是請求時 Origin字段的值,要麼是一個 *,表示接受任意域名的請求
  • Access-Control-Allow-Credentials: 表示是否容許客戶端發送 Cookie,是一個布爾值。默認狀況下,Cookie不包括在 CORS 請求之中,設爲 true,即表示服務器明確許可,Cookie 能夠包含在請求中,一塊兒發給服務器
  • Access-Control-Expose-Headers: CORS請求時,XMLHttpRequest對象的getResponseHeader()方法只能拿到6個基本字段,自定義的header字段是拿不到的,若是想拿到自定義的Header 字段,就必須在 Access-Control-Expose-Headers裏面指定

非簡單請求

非簡單請求的 CORS 請求,會在正式通訊以前,增長一次 HTTP查詢請求,稱爲 "預檢"請求。下面是一個預檢請求的HTTP頭信息:

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

除了 Origin字段外,還包括兩個特殊字段:

  • Access-Control-Request-Method: 用來列出瀏覽器的 CORS請求用到哪些HTTP方法
  • Access-Control-Request-Headers: 該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header

預檢請求的迴應

服務器收到預檢請求後,作出迴應:

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://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
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
複製代碼

下面總結下,預檢請求下的迴應的與CORS相關的請求頭:

  • Access-Control-Allow-Methods: 逗號分隔的字符串,代表服務器支持的全部跨域請求的方法。注意是全部方法,不是單個瀏覽器請求時的那個方法,這是爲了不屢次 "預檢"請求
  • Access-Control-Allow-Headers:若是瀏覽器請求包括 Access-Control-Request-Headers字段,則 Access-Control-Allow-Headers是必須的,它代表服務器支持的全部頭信息字段,不限於瀏覽器再預檢中請求的字段
  • Access-Control-Max-Age: 該字段可選,用來指定本次預檢請求的有效期,單位爲秒。

實現 CORS 跨域請求的方式

對於 CORS的跨域請求,主要有如下幾種方式可供選擇:

  • 返回新的CorsFilter
  • 重寫 WebMvcConfigurer
  • 使用註解 @CrossOrigin
  • 手動設置響應頭 (HttpServletResponse)

注意:

  • CorFilter / WebMvConfigurer / @CrossOrigin 須要 SpringMVC 4.2以上版本才支持,對應於springBoot 1.3版本以上
  • 上面前兩種方式屬於全局 CORS 配置,後兩種屬性局部 CORS配置。若是使用了局部跨域是會覆蓋全局跨域的規則,因此能夠經過 @CrossOrigin 註解來進行細粒度更高的跨域資源控制

1.返回新的 CorsFilter(全局跨域)

在任意配置類,返回一個 新的 CorsFIlter Bean ,並添加映射路徑和具體的CORS配置路徑。

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域
        config.addAllowedOrigin("*");
        //是否發送 Cookie
        config.setAllowCredentials(true);
        //放行哪些請求方式
        config.addAllowedMethod("*");
        //放行哪些原始請求頭部信息
        config.addAllowedHeader("*");
        //暴露哪些頭部信息
        config.addExposedHeader("*");
        //2. 添加映射路徑
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);
        //3. 返回新的CorsFilter
        return new CorsFilter(corsConfigurationSource);
    }
}

複製代碼

2. 重寫 WebMvcConfigurer(全局跨域)

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否發送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedOrigins("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}
複製代碼

3. 使用註解 (局部跨域)

在控制器上使用註解 @CrossOrigin:

@RestController
@CrossOrigin(origins = "*")
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello world";
    }
}

複製代碼

在方法上使用註解 @CrossOrigin:

@RequestMapping("/hello")
    @CrossOrigin(origins = "*")
    public String hello() {
        return "hello world";
    }
複製代碼

4. 手動設置響應頭(局部跨域)

使用 HttpServletResponse 對象添加響應頭(Access-Control-Allow-Origin)來受權原始域,這裏 Origin的值也能夠設置爲 "*",表示所有放行。

@RequestMapping("/index")
    public String index(HttpServletResponse response) {
        response.addHeader("Access-Allow-Control-Origin","*");
        return "index";
    }
複製代碼

參考資料 & 鳴謝

相關文章
相關標籤/搜索