SpringBoot 優雅解決 ajax 跨域請求

今天打開頁面報錯,地址是有效的,但瀏覽器會報 "No 'Access-Control-Allow-Origin' header is present on the requested resource " 錯誤頁面以下: web

1568621277875

這是因爲 ajax 跨域請求形成的ajax

什麼是跨域

因爲瀏覽器同源策略(同源策略,它是由 Netscape 提出的一個著名的安全策略。如今全部支持JavaScript 的瀏覽器都會使用這個策略。所謂同源是指域名、協議、端口相同),凡是發送請求 url 的協議、域名、端口三者之間任意一與當前頁面地址不一樣即爲跨域。spring

How does it works ?

CORS請求(包括預選的帶有選項方法)被自動註冊到各類 HandlerMapping 。他們處理 CROS 準備請求並攔截 CORS 簡單和實際請求,這得益於 CorsProcessor 實現(默認狀況下默認DefaultCorsProcessor 處理器),以便添加相關的CORS響應頭(如 Access-Control-Allow-Origin )。 CorsConfiguration 容許您指定CORS請求應該如何處理:容許 origins, headers, methods 等。api

a、AbstractHandlerMapping#setCorsConfiguration() 容許指定一個映射,其中有幾個CorsConfiguration 映射在路徑模式上,好比/api/**。跨域

b、子類能夠經過重寫AbstractHandlerMapping類的getCorsConfiguration(Object, HttpServletRequest)方法來提供本身的CorsConfiguration。瀏覽器

c、處理程序能夠實現 CorsConfigurationSource 接口(如ResourceHttpRequestHandler),以便爲每一個請求提供一個CorsConfiguration。安全

如何解決

普通跨域請求解決方案

1. 請求接口添加註解 @CrossOrigin(origins = "*", maxAge = 3600)bash

說明:origins = "*" origins 值爲當前請求該接口的域mvc

2. 通用配置app

/**
     * 跨域請求配置
     * @author chendesheng
     * @create 2019/9/16 19:07
     */
    @Configuration
    public class CorsConfig {
    
        @Bean
        public CorsFilter corsFilter(){
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**",buildconfig());
            return new CorsFilter(source);
        }
    
        private CorsConfiguration buildconfig(){
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("*");
            corsConfiguration.addAllowedHeader("*");
            corsConfiguration.addAllowedMethod("*");
            return corsConfiguration;
        }
    
    }
複製代碼

ajax自定義headers的跨域請求

$.ajax({
            type:"GET",
            url:"http://localhost:8766/main/currency/sginInState",
            dataType:"JSON",
            data:{
                uid:userId
            },
            beforeSend: function (XMLHttpRequest) {
                XMLHttpRequest.setRequestHeader("Authorization", access_token);
            },
            success:function(res){
                console.log(res.code)
            }
        })
複製代碼

此處請求報錯:OPTIONS http://localhost:8766/main/currency/sginInState 500

普通跨域的解決方案已經沒法解決這種問題,爲何會出現OPTIONS請求呢?

緣由:

瀏覽器會在發送真正請求以前,先發送一個方法爲OPTIONS的預檢請求 Preflighted requests 這個請求是用來驗證本次請求是否安全的,可是並非全部請求都會發送,須要符合如下條件:

  • 請求方法不是GET/HEAD/POST
  • POST 請求的 Content-Type 並不是 application/x-www-form-urlencoded, multipart/form-data 或text/plain
  • 請求設置了自定義的header字段

對於管理端的接口,我有對接口進行權限校驗,每次請求須要在header中攜帶自定義的字段(token),因此瀏覽器會多發送一個 OPTIONS 請求去驗證這次請求的安全性。

爲什麼 OPTIONS 請求是500呢?

OPTIONS請求只會攜帶自定義的字段,並不會將相應的值帶入進去,然後臺校驗 token 字段時 token 爲 NULL,因此驗證不經過,拋出了一個異常。

如何解決這個異常?

1. spring boot項目application.yml中添加

spring:
mvc:
dispatch-options-request: true 
複製代碼

2. 添加過濾器配置

第一步:手寫 RequestFilter 請求過濾器配置類此類須要實現 HandlerInterceptor 類,HandlerInterceptor 類是 org.springframework.web.servlet.HandlerInterceptor 下的。

具體代碼以下:

@Component
public class RequestFilter implements HandlerInterceptor {
   public boolean preHandler(HttpServletRequest request,HttpServletResponse response,Object handler){
       response.setHeader("Access-Control-Allow-Origin", "*");
       response.setHeader("Access-Control-Allow-Credentials", "true");
       response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS");
       response.setHeader("Access-Control-Max-Age", "86400");
       response.setHeader("Access-Control-Allow-Headers", "Authorization");
       // 若是是OPTIONS請求則結束
       if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {
           response.setStatus(HttpStatus.NO_CONTENT.value());
           return false;
       }
       return true;
   }
}
複製代碼

第二步:手寫 MyWebConfiguration 此類須要繼承 WebMvcConfigurationSupport 。

具體代碼實現:

@Component
public class MyWebConfiguration extends WebMvcConfigurationSupport{
    @Resource
    private RequestFilter requestFilter;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 跨域攔截器
        registry.addInterceptor(requestFilter).addPathPatterns("/**");
    }
}
複製代碼

這樣就優雅的解決了 ajax 跨域請求的問題。

相關文章
相關標籤/搜索