1.背景
今天给自己的demo (SpringBoot + Vue) 加了个请求头,也就是前端直接传过来,然后后台统一获取,想着这样更优雅,所以是用的拦截器实现,然后就导致后台默认的跨域配置失效,前端报了跨域问题(在浏览器控制台)
2.出现错误
2.1.响应码问题
Access to XMLHttpRequest at 'http://localhost:8001/memo/user/info' from origin 'http://localhost:9528' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
后台报错:拦截器 preHandle 抛异常,正是抛异常的地方
浏览器网络报错:响应码为500
浏览器控制台报错:控制台报错 It does not have HTTP ok status2.1.原因分析
基础知识!
对于浏览器的请求,浏览器会根据自己的判断分为2种,一种是简单请求,另一种是非简单请求,对于非简单请求,浏览器会先发送一个OPTIONS请求(是不会带参数的),返回成功之后再发送原本的请求
由于配置了拦截器,当OPTIONS请求走到拦截器逻辑时,由于没有带参数,所以抛出了异常,这样就没有返回成功 http 状态码,所以原本的POST请求就不会发出
2.2.响应头缺少问题
Access to XMLHttpRequest at 'http://localhost:8001/memo/user/login' from origin 'http://localhost:9528' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
后台代码:由于之前是OPTIONS请求返回500,所以想着在Filter将请求返回成功,所以OPTIONS问题解决了
浏览器网络报错:由于 Filter 直接给 OPTIONS 返回成功,所以现在请求卡在了原本的请求
浏览器控制台报错:跨域问题,缺少响应头 No 'Access-Control-Allow-Origin' header is present on the requested resource.2.2.原因分析
虽然解决了OPTIONS问题,但是在实际请求处理的时候,由于在Interceptor 处理逻辑抛出异常,导致了跨域的处理逻辑没有执行,所以响应头缺少信息,浏览器就报错了
3.解决方案
3.1.原本的跨域配置
这种配置在没有自定义拦截器的时候没问题,但是有了拦截器之后,如果拦截器中出现异常,就可能导致
① OPTIONS 请求返回失败
② 跨域处理不执行,响应缺少跨域的头,浏览器就会报错
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET","POST","HEAD","PUT","DELETE")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
3.2.最终的配置
使用 Filter 的方式做跨域,因为 Filter 的执行是在 interceptor 的外层
① 所以即使 Interceptor 里面抛异常,也不会影响 Filter 的跨域处理,照样会执行
② 而且由于 Filter 处理逻辑会早于 Interceptor 执行,上述方法会直接将 OPTIONS 请求直接返回,所以不会再出现 OPTIONS 请求失败的情况
/**
* 跨域处理
* 因为 WebMvcConfigurer 自带的跨域配置,在有拦截器的情况下,有可能执行不到
* 所以使用 Filter 的方式,比拦截器先执行,不会受拦截器影响
*/
@Bean
public CorsFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOriginPattern("*");
config.setAllowCredentials(true);
config.addAllowedMethod("*");
config.addAllowedHeader("*");
config.addExposedHeader("token");
UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
configSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configSource);
}