在公司開發過程中,一個前后端分離的項目遇見了跨域的問題。
前端控制台報錯:No 'Access-Control-Allow-Origin' header is present on the requested resource.
從經驗得知:spring boot解決跨域問題。兩種解決方法:
1、重寫 WebMvcConfigurer 類,並注入到spring容器中:
@Configuration public class CustomCorsConfiguration implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT") .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers") .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials") .allowCredentials(true).maxAge(3600); } }
這個底層我猜想是一種攔截器,他的優先級在過濾器之后。也就是說執行完過濾器,才會執行這個攔截器。
2、在@Configuration中,注入CorsFilter:
@Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig()); return new CorsFilter(source); } private CorsConfiguration corsConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); // 請求常用的三種配置,*代表允許所有,也可以自定義屬性(比如 header 只能帶什么,只能是 post 方式等) corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setAllowCredentials(true); corsConfiguration.setMaxAge(3600L); return corsConfiguration; }
這個底層就是過濾器,想要獲得服務器的跨域允許,必須經過此CorsFilter。
不過,在嘗試了兩種方法發現,why?我的項目還是報錯:No 'Access-Control-Allow-Origin' header is present on the requested resource.
在使用第二種方法,並經過調試后發現了問題,CorsFilter 的執行順序在token過濾器的后面!
這時候再來了解下跨域的原理,詳見 http://www.ruanyifeng.com/blog/2016/04/cors.html
我這邊前端是屬於跨域中的非簡單請求,他的過程是:在前端真正發送請求api接口的http時,會發出一個類似於嗅探的假請求給服務器,這個假請求會從服務器攜帶信息,告知頁面是否允許頁面跨域訪問,如果允許,則發出真正的http,如果不,瀏覽器控制台直接報錯。
我這個項目的問題就出在這。如果這個嗅探沒到達CorsFilter,就被服務器攔截下來了,那么他得到的信息肯定是 不允許跨域訪問 ! 我們的嗅探就是被項目中的jwt過濾器攔截下來了,因為我在項目中的設定是每個請求必須攜帶jwt的token,否則被攔截下來,而嗅探是一種瀏覽器生成的簡單請求,肯定不攜帶jwt token,因此被攔截下來了,也就沒法到達CorsFilter,就沒法獲得跨域的許可。所以在使用百度的第二種方法就失敗了!
那么第一種百度的方法失敗的原因呢?更簡單了!因為第一種底層是攔截器(WebMvcConfigurer),他的執行優先級在過濾器之后,所以嗅探的http在到這個攔截器的時候,早就被jwt過濾器擋下來了,因此提示跨域失敗!
聽了上述分析,是否解決思路已經有了呢?對,只要我們的嗅探http到達CorsFilter過濾器或者攔截器(WebMvcConfigurer)就行了,但是我們的jwt過濾必須得要,所以我的解決方法是,修改CorsFilter的優先級:
@Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", corsConfig()); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); //*****這里設置了優先級***** bean.setOrder(1); return bean; } private CorsConfiguration corsConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); // 請求常用的三種配置,*代表允許所有,也可以自定義屬性(比如 header 只能帶什么,只能是 post 方式等) corsConfiguration.addAllowedOrigin("*"); corsConfiguration.addAllowedHeader("*"); corsConfiguration.addAllowedMethod("*"); corsConfiguration.setAllowCredentials(true); corsConfiguration.setMaxAge(3600L); return corsConfiguration; }
完美解決!