跨域CORS圖片上傳問題

寒武沒有紀發表於2018-11-01

異常問題

專案中存在跨域圖片上傳請求,提示下列錯誤

Access to XMLHttpRequest at ‘http://localhost:8080/common/uploadPic’ from origin 'http://localhost:8000’has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

最初的跨域支援使用spring提供的註解 @CrossOrigin 來處理,針對專案中普通介面 @CrossOrigin註解的預設策略已經可以滿足,唯獨對於圖片上傳請求無法支援。

檢視 Spring CORS 支援文件,@CrossOrigin 註解預設策略支援所有的origins 以及 GETHEADPOST 三種請求方法。

In addition to fine-grained, annotation-based configuration you’ll probably want to define some global CORS configuration as well. This is similar to using filters but can be declared withing Spring MVC and combined with fine-grained @CrossOrigin configuration. By default all origins and GET, HEAD and POST methods are allowed.

CORS機制

CORS請求

瀏覽器將 CORS 請求分成兩類:簡單請求( simple request )和非簡單請求( not-so-simple request )。

只要同時滿足以下兩大條件,就屬於簡單請求。(引用自跨域資源共享 CORS 詳解

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

  • HEAD
  • GET
  • POST

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

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限於三個值application/x-www-form-urlencodedmultipart/form-datatext/plain

預檢請求

因為前端H5頁面請求時新增了全域性配置 Content-Typeapplication/json ,不符合上述簡單請求條件,屬於非簡單請求,因此會新增預檢 OPTIONS 請求

預檢 OPTIONS 請求 header 資訊

Request Method: OPTIONS
Access-Control-Request-Headers: x-requested-with
Access-Control-Request-Method: POST
Origin: http://test.demo.com:59004
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36

預檢請求響應

瀏覽器收到預檢請求響應後,會檢查 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 確認允許跨域請求。

Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-requested-with
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Origin: http://test.demo.com:59004
Connection: keep-alive
Content-Length: 0
Date: Thu, 01 Nov 2018 07:06:30 GMT
Server: nginx
Vary: Origin

異常解決

因此決定在 @CrossOrigin 註解中配置支援 OPTIONS 請求,再次本地測試跨域圖片上傳,可以成功上傳。

@CrossOrigin(methods = {RequestMethod.GET, RequestMethod.POST, RequestMethod.OPTIONS})

但是部署測試環境之後,發現了另一個潛在的問題,登入環境開啟了登入攔截,會使用 cookie 中登入資訊檢驗登入,當登入資訊在有效期內,所有介面都能正常跨域請求使用。但登入資訊失效後,導致所有的介面包括圖片上傳都提示開頭的跨域問題,懷疑是 @CrossOrigin 註解的執行順序在登入攔截器之後,當請求被攔截器攔截,因為還未執行 @CrossOrigin 跨域支援邏輯,導致響應就存在異常,也不能正確跳轉登入頁面。因此嘗試尋找另外的跨域處理方式,在 Spring CORS 支援文件中提供了Spring Boot應用基於 Filter 方式的全域性跨域支援。

@Configuration
public class CorsConfig {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod(HttpMethod.GET);
        config.addAllowedMethod(HttpMethod.POST);
        config.addAllowedMethod(HttpMethod.OPTIONS);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

修改為 Filter 全域性跨域支援後,再次測試包括登入有效期內及登入失效情況下圖片上傳和其它常規介面,都能正常處理,登入失效時也能正常返回跳轉登入的業務狀態碼。

參考資料

CORS Support in Spring Framework
http://www.ruanyifeng.com/blog/2016/04/cors.html

相關文章