likes
comments
collection
share

什么情况下Spring的CorsFilter会失效

作者站长头像
站长
· 阅读数 18

1. 背景分析

众所周知CorsFilter是Spring提供的跨域过滤器,我们可能会做以下的配置,基本上就是允许任何跨域请求

@Configuration
public class CorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOriginPattern("*");
        //允许跨越发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        //跨域允许时间,单位:秒
        config.setMaxAge(60 * 60L);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

我们的系统主要是SpringBoot+SpringSecurity构成的,我利用Spring的CorsFilter做跨域操作也没问题吧

但就是在这种简单的情况下前端还是爆出了跨域情况

什么情况下Spring的CorsFilter会失效

这就头大了呀,毕竟网上的教程和我的经验分析都觉得是可以的

什么情况下Spring的CorsFilter会失效

2. 问题分析

这个时候就没办法了,只能本地Debug + 分析源码,看看问题所在地

然后我是利用Apifox里面保存的请求实例,然后复制一份(这里面有系统的认证参数),请求方式改为了OPTIAN了,然后其他杂七杂八的请求头也从前端中复制到新的请求实例中了

然后启动项目,打好断点

什么情况下Spring的CorsFilter会失效

最终发现进入了CorsFilter的doFilterInternal方法,并且判断成功,确实是一个OPTIAON, 响应头也已经填充了对应的响应头,ApiFox也提示请求正常

这就离谱了,dev环境不可以,local环境就可以了,这个时候我决定不偷懒了,新建一个请求实例,完全照搬前端发起的请求

这个时候问题就稍微浮出水面了,我发现CorsFilter都没进去,Apifox就已经提示了跨域了

于是我继续分析向上的流程,大家看下面的这个图,这是注册到Tomcat中的过滤器组成的过滤器链

什么情况下Spring的CorsFilter会失效

先看前三个过滤器:

  • OrderedCharacterEncodingFilter:确认本次请求的编码格式
  • OrderedCharacterEncodingFilter:为PUT、PATCH、DELETE这样的请求方式,将请求体转为键值对形式,然后通过 getParameter 读取
  • OrderedRequestContextFilter:用于初始化 LocaleContextHolder 和 RequestContextHolder 的过滤器

然后我们再看后面的springSecurityFilterChaincorsFilter,注意这里是springSecurityFilterChain在前,也就是会先执行

我最新的这一次跨域请求是没有携带认证参数的,也就是说会被SpringSecurity的ExceptionTranslationFilter处理访问被拒绝异常,也就不会走后面的corsFilter,也就不会写入响应头的相关参数了

3. 解决办法

3.1 SpringSecurity的角度

问题分析到这其实解决思路就很简单了,要么我们改用SpringSecurity提供的跨域支持,就像下面这样

什么情况下Spring的CorsFilter会失效

3.2 SpringBoot的角度

要么我们就改变注册到Tomcat的CorsFilter的执行顺序,我当时也是这样处理的,看下图我介绍的三个过滤器均在 springSecurityFilterChain之前执行,并且他们的顺序是保持一致的,也就是SpringBoot一定做了排序的

什么情况下Spring的CorsFilter会失效

我们可以先看这三个过滤器的共同点:他们都同时实现了OrderedFilter接口,并且重写getOrder()方法

什么情况下Spring的CorsFilter会失效

在这种情况下,我就自己写了一个CorsFilter, 并实现了OrderedFilter

什么情况下Spring的CorsFilter会失效

然后我们就可以发现过滤器的顺序发生了变化

什么情况下Spring的CorsFilter会失效

前端也正常的发起了跨域请求,业务流程也可以继续跑了

什么情况下Spring的CorsFilter会失效

4. 总结

总结:

  • 其实跨域问题很好解决,本质上就是后端要设置对应的响应头
  • 我这里出现问题还是因为自己偷懒了,直接复制以前的请求实例发起OPTION请求
  • 还有就是没想起来SpringBoot和SpringSecurtiy中处理跨域会有一个先后顺序(冲突)的问题才导致的
  • 当然Spring中处理跨域其实不只CorsFilter这一种方式,还可以通过RequestMappingHandlerMapping完成,就在下面的地方,这个后续在介绍

什么情况下Spring的CorsFilter会失效