1. 程式人生 > 其它 >淺析SpringSecurity對跨域非簡單請求的Prefight預檢請求的處理:requestMatchers(CorsUtils::isPreFlightRequest).permitAll()、及簡單請求與非簡單請求的理解

淺析SpringSecurity對跨域非簡單請求的Prefight預檢請求的處理:requestMatchers(CorsUtils::isPreFlightRequest).permitAll()、及簡單請求與非簡單請求的理解

  先說解決方案吧,我們程式碼裡的解決方案:

.antMatchers("/examRoom/find").permitAll()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()  // 關鍵這行,就是允許Prefight預檢請求
.antMatchers(HttpMethod.POST, "/login").permitAll()

  Prefight預檢請求就是一個 options 請求,我們可以點進去看原始碼:

public static boolean isPreFlightRequest(HttpServletRequest request) {
    
return HttpMethod.OPTIONS.matches(request.getMethod()) && request.getHeader("Access-Control-Request-Method") != null; }

  requestMatchers(CorsUtils::isPreFlightRequest).permitAll()的作用是將PreflightRequest不做攔截。

一、為什麼需要preflight request

  我們都知道瀏覽器的同源策略,就是出於安全考慮,瀏覽器會限制從指令碼發起的跨域HTTP請求,像XMLHttpRequest和Fetch都遵循同源策略。

  瀏覽器限制跨域請求一般有兩種方式:(1)瀏覽器限制發起跨域請求;(2)跨域請求可以正常發起,但是返回的結果被瀏覽器攔截了。

  一般瀏覽器都是第二種方式限制跨域請求,那就是說請求已到達伺服器,並有可能對資料庫裡的資料進行了操作,但是返回的結果被瀏覽器攔截了,那麼我們就獲取不到返回結果,這是一次失敗的請求,但是可能對資料庫裡的資料產生了影響。

  為了防止這種情況的發生,規範要求,對這種可能對伺服器資料產生副作用的HTTP請求方法,瀏覽器必須先使用OPTIONS方法發起一個預檢請求,從而獲知伺服器是否允許該跨域請求:如果允許,就傳送帶資料的真實請求;如果不允許,則阻止傳送帶資料的真實請求

  瀏覽器將CORS請求分成兩類:簡單請求和非簡單請求。

  瀏覽器對這兩種請求的處理是不一樣的,非簡單請求的CORS請求,會在正式通訊之前,增加一次HTTP查詢請求,稱為"預檢"請求(preflight)。

二、簡單請求與非簡單請求

  詳見之前總結的這篇部落格:淺析http簡單請求與複雜請求

  CORS請求,瀏覽器與伺服器互動的過程,這上面的坑主要就是Preflight。如果我們的後臺用了安全管理框架(比如Spring Security),並且沒有對Preflight這個請求做出相應的處理,那麼這個請求會導致許可權管控失敗(比如無法登入)。

  因為Preflight不攜帶Cookie,即不攜帶JSESSIONID,因此Spring Security攔截器會認為你沒有登入。

  由於 Prefight 預檢請求,實際就是一個 options 請求,所以我們也可以允許所有 options 請求,這樣解決這個問題。

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and()
        //跨域請求會先進行一次options請求,允許 options 請求
        .antMatchers(HttpMethod.OPTIONS).permitAll()
        ...
}

三、簡單請求與非簡單請求的請求流程

  詳細內容看這篇文件,官方描述:Cross-Origin Resource Sharing (CORS):https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS

1、簡單請求

  這在客戶端和伺服器之間執行簡單的交換,使用 CORS 標頭來處理許可權:

2、預檢請求

  與“簡單請求”(上面討論過)不同,對於“預檢”請求,瀏覽器首先使用該OPTIONS方法向另一個源上的資源傳送 HTTP 請求,以確定實際請求是否可以安全傳送。跨站點請求是這樣預檢的,因為它們可能會對使用者資料產生影響。