环境
前端
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; //这个请求就这样执行完了 } } ...................... }