记一次Spring Boot 跨域支持问题

项目版本:Spring boot 2.1.1.RELEASE

问题描述:配置的跨域支持失效。

配置方法:Application实现WebMvcConfigurer接口然后重写addCorsMappings方法。

其他配置:同时还是实现了addInterceptorsaddArgumentResolvers这两个方法。

确定问题实验步骤

  • 取消其他配置,发现跨域生效,初步猜测问题发生在,另外两个配置实现中。
  • 取消addArgumentResolvers,放开addInterceptors,失效。
  • 取消addInterceptors,放开addArgumentResolvers,有效。

结合上述问题,初步断定问题出现在自定义的HandlerInterceptor中。

自定义的拦截器里面,从header中获取 token 然后进行登录用户的判断。如果验证失败则抛出异常,在ControllerAdvice中进行统一的异常处理。

  • 将其他逻辑注释,直接return true,发现配置生效。缩小问题范围。如果不返回true, Spring 会做什么呢。这时自然想到DispatcherServlet

debug 跟进源码中,发现在 line 1033时,如果有一个拦截器返回false就直接结束了逻辑,如果抛出异常,后面的拦截器的逻辑也不执行了。

但是现在并不能说明跨域不生效的问题。这时找到CorsRegistry查看这个类到底被 Spring 施展了什么魔法。

CorsRegistry中有一个getCorsConfigurations,然后定位到WebMvcConfigurationSupport中调用了这个方法。分别在一下方法中进行了设置。

  • 1
    2
    3
    4
    requestMappingHandlerMapping
    viewControllerHandlerMapping
    beanNameHandlerMapping
    resourceHandlerMapping

    结合调用接口时,DispatcherServletdoDispatch方法中的mappedHandler.interceptorList,有如下四个 Interceptor。

    • 自定义拦截器
    • ConversionServiceExposingInterceptor
    • ResourceUrlProviderExposingInterceptor
    • AbstractHandlerMapping$CorsInterceptor

    至此,断案。自定义的拦截器会第一个执行preHandle,抛出异常或者 return false后,后面的拦截器都没有执行,所以我们设置的跨域支持的相关配置也没有生效。

解决方法

  • 使用Filter
  • 使用 InterceptorRegistrationorder方法将自定义的拦截器配置到AbstractHandlerMapping$CorsInterceptor后面,第四个位置order(4),有效。

如果使用了nginx做反向代理服务器。

1
2
proxy_pass  http://127.0.0.1:8080;
proxy_set_header Host $host;

相当于进行一次转发。

二者方法各有千秋,技术上很多事情都是取舍,因为我们没有过 nginx进行跨域支持的配置,而且通过业务配置也能解决问题,这里不进行延展的探讨。两种方式我都可以接受。

留言

⬆︎TOP