環境
前端
vue + axios
後端
springboot
問題與方案
這裏可以看文獻的《跨域資源共享》;請求分爲簡單請求和複雜請求,簡單請求直接進行正式請求,而複雜請求則會在正式請求之前發送請求方式爲OPTIONS的預檢請求。
跨域解決方案(針對複雜請求)
第一種
原因: 缺少跨域的Header標頭
解決:
// 實現 implements WebMvcConfigurer 接口
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedOrigins("http://localhost:8080")
.allowedMethods("GET","POST","DELETE","PUT","OPTIONS")
.allowedHeaders("*");
// .exposedHeaders("Allow"); //這一句可以不需要,暴露指定的header
}
第二種
原因: (OPTIONS)預檢請求返回的不是OK的狀態碼(200)
解決: 解決後臺的報錯問題即可
第三種
由於我是集成了SpringSecurity,那麼只需添加以下代碼片段即可
/** * 注入CORS配置源 * @see WebMvcConfiguration#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry) */ @Resource private CorsConfigurationSource corsConfigurationSource; protected void configure(HttpSecurity http) throws Exception http.cors().configurationSource(corsConfigurationSource) }
如果沒有集成SpringSecurity,那麼添加以下代碼(未測試)
/**
* 注入CORS配置源
* @see WebMvcConfiguration#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry)
*/
@Resource
private CorsConfigurationSource corsConfigurationSource;
@Bean
public FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean(){
FilterRegistrationBean<CorsFilter> corsFilterFilterRegistrationBean = new FilterRegistrationBean<>();
corsFilterFilterRegistrationBean.setUrlPatterns(Collections.singleton("/*"));
corsFilterFilterRegistrationBean.setOrder(0); // 這裏調整的執行次序,按自己所需的值修改
corsFilterFilterRegistrationBean.setFilter(new CorsFilter(configSource));
corsFilterFilterRegistrationBean.setEnabled(true);
return corsFilterFilterRegistrationBean;
}
第四種:忽略OPTIONS預檢請求
//註冊一個 Filter 過濾器,對OPTIONS請求直接返回即可
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.addHeader("Allow", ""); //這個Allow有大用
return false;
}
/**
* Security的自定義認證過濾器實現,此爲參考即可;只列出了主要代碼以供參考
*/
public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
/**
* 此方法返回是否需要認證
* @param request
* @param response
* @return
*/
@Override
protected boolean requiresAuthentication(HttpServletRequest request, HttpServletResponse response) {
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.addHeader("Allow", "");
return false;
}
return super.requiresAuthentication(request, response);
}
...
}
/**
只列出了主要代碼以供參考;可以在doFilter方法裏過濾OPTIONS預檢請求
*/
public class JwtOnceAuthenctionFilter extends OncePerRequestFilter {
/**
此方法的返回值只決定了執行不執行當前過濾器而已,這裏只是位置,並未加代碼,可參考上方忽略
預檢請求(OPTIONS)的片段
*/
@Override
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return super.shouldNotFilter(request);
}
...
}
response.addHeader("Allow", "");
這裏說下這句代碼的用意,主要就是終結這個請求的執行而已/** 留下代碼定位,Debug看吧 org.springframework.web.servlet.FrameworkServlet#doOptions */ @Override protected void doOptions(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if (this.dispatchOptionsRequest || CorsUtils.isPreFlightRequest(request)) { processRequest(request, response); //這裏會判斷響應Header是否包含`Allow`,值目前並沒有找到需要的地方 if (response.containsHeader("Allow")) { // 來自處理程序的正確OPTIONS響應-我們完成了。 return; //這個請求就這樣執行完了 } } ...................... }