CORS的概念
CORS問(wèn)題的關(guān)鍵在于互例,一個(gè)跨源請(qǐng)求會(huì)被拆分為兩次欺劳,多一次OPTIONS绑雄,也就是說(shuō)發(fā)送一個(gè)Get展辞,將會(huì)有一個(gè)OPTIONS先發(fā)送到服務(wù)端,具體流程可參考標(biāo)準(zhǔn)定義万牺。接下來(lái)描述的問(wèn)題也只在于需要了解到這一步罗珍。
Spring Security
Spring Security的關(guān)鍵在于Filter,所有符合條件的請(qǐng)求脚粟,都會(huì)被DelegatingFilterProxyRegistrationBean所攔截覆旱,而真實(shí)被調(diào)用的Filter卻是名稱為springSecurityFilterChain(FilterChainProxy)的FilterChain所攔截。
服務(wù)端配置CORS
-
通過(guò)WebMvcConfigurerAdapter#addCorsMappings去配置
public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedHeaders("*") .allowedMethods("*") .allowedOrigins("*"); } }
-
通過(guò)自定義Filter
@Bean public FilterRegistrationBean corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.addAllowedOrigin("*"); config.setAllowCredentials(true); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(0); return bean; }
這兩種方式實(shí)現(xiàn)的機(jī)制完全不一樣核无,也就是產(chǎn)生作用的生命周期不一樣扣唱。
- 方式一,WebMvcConfigurerAdapter的配置最終將會(huì)轉(zhuǎn)換為RequestMappingHandlerMapping团南,而這個(gè)Handler最終是被DispatcherServlet調(diào)用噪沙,也就是說(shuō),方式一的配置將會(huì)在Servlet中被調(diào)用吐根。
- 方式二正歼,很容易理解,配置的Filter將會(huì)被springboot自動(dòng)配置到Tomcat或其他web容器中佑惠。ServletContextInitializerBeans#addAdaptableBeans方法中朋腋,將自動(dòng)查找spring容器中存在的Filter實(shí)現(xiàn),并且根據(jù)@Order或Order來(lái)判斷Filter的排序膜楷。
沖突
keycloak場(chǎng)景
前端已經(jīng)通過(guò)javascript的接口旭咽,從keycloak驗(yàn)證并獲得了token,keycloak作為單點(diǎn)登錄系統(tǒng)赌厅,理論上是可以通過(guò)token登錄并驗(yàn)證任何一個(gè)后端服務(wù)穷绵,但是,當(dāng)按照文檔上面的描述特愿,正確配置springboot及security仲墨,后端依然無(wú)法通過(guò)token的驗(yàn)證。但當(dāng)將前后端使用同一個(gè)IP和端口時(shí)揍障,請(qǐng)求正常目养。
解決過(guò)程
- CORS同源配置。如上配置CORS毒嫡,但請(qǐng)求依然癌蚁。
- 打開瀏覽器debug,發(fā)現(xiàn)OPTIONS請(qǐng)求直接返回401,授權(quán)失敗努释。此時(shí)如果先前已經(jīng)了解CORS就不會(huì)產(chǎn)生疑問(wèn)碘梢,CORS會(huì)將任何請(qǐng)求先切分為一個(gè)OPTIONS和一個(gè)原來(lái)的。
- 上一步表面OPTIONS請(qǐng)求被授權(quán)服務(wù)攔截伐蒂,那么解決問(wèn)題的方式就出來(lái)了煞躬。
解決方案
-
方案一,配置Spring security策略逸邦,不攔截OPTIONS請(qǐng)求
HttpSecurity#authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
方案二恩沛,自定義CorsFilter,設(shè)置order為最高優(yōu)先級(jí)或者其他昭雌,只需要優(yōu)先級(jí)比Spring security的order高便好复唤。