CORS过滤器优先级不对,预检请求正常,正式请求报错CORS跨域

2023-05-16

环境信息

Spring Boot:2.0.8.RELEASE

Spring Boot内置的tomcat:tomcat-embed-core 8.5.37

问题描述

在使用浏览器访问应用,给服务端发送请求的时候,前端没有收到正确的响应,浏览器控制台和网络都报错了,提示是CORS跨域了,看接口返回的Headers里确实没有允许跨域的项(Access-Control-Allow-Origin等)。

报错提示:

响应头:

错误信息:

Access to XMLHttpRequest at 'http://localhost:6001/tfb-biz-common-service-app/query/commonQuery' from origin 'http://localhost:8804' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
POST http://localhost:6001/tfb-biz-common-service-app/query/commonQuery net:: ERR_FAILED 200

解决方案

解决思路

乍一看是CORS跨域问题,可是服务端已经进行了CORS跨域设置,之前运行也都一切正常。

打开服务器日志一看,后端在处理请求的时候,报错了,不过错误信息不是CORS跨域错误,而是用户长时间未操作:

而浏览器显示的错误是CORS跨域,这就涉及到浏览器的跨域资源共享(CORS)、同源策略规则了:

在服务端没有返回Access-Control-Allow-Origin时,浏览器端无法正常的加载服务器端响应的内容,即:服务端响应了数据(比如这里的报错信息:用户长时间未操作,请重新登录),可是响应头里没有Access-Control-Allow-Origin项,浏览器不能显示出(用户长时间未操作,请重新登录)这些信息,而是提示CORS错误。

还有一个问题,这种复杂请求,是会先发送预检请求的(OPTIONS),可以通过在浏览器网络选项卡里的筛选设置选中All来查看所有的请求:

响应头等信息:

奇怪的是,预检请求有返回Access-Control-Allow-Origin信息,并没有引发CORS错误。

原因在于,服务端的CORS设置,是在CorsFilter过滤器里判断后,添加到响应头里的。而正式请求还未执行到CorsFilter,在执行EfFilter时就已经遇到错误,响应给浏览器了,因此响应头里不包含CORS信息。

解决方法

根据前面的分析,是因为CorsFilter过滤器的执行顺序不对(优先级太低)了,导致响应头不包含CORS信息,浏览器提示CORS跨域而不显示服务端真正返回的内容。

那么只要将CorsFilter过滤器的优先级调高就行了。

原先的配置:

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CorsConfig {
    private static Log log = LogFactory.getLog(CorsConfig.class);

    @Value("${eframework.cors.allowed.origins:*}")
    private String origins;

    @Bean
    public CorsFilter CorsConfig() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        List<String> exposedHeaders = Arrays.asList("x-auth-token", "content-type", "X-Requested-With", "XMLHttpRequest");
        List<String> allowedOrigins = Arrays.asList(StringUtils.split(origins, ","));

        List<String> allowedHeaders = Arrays.asList("*");
        List<String> allowedMethods = Arrays.asList("*");
        corsConfig.setAllowedHeaders(allowedHeaders);
        corsConfig.setAllowedMethods(allowedMethods);
        corsConfig.setAllowedOrigins(allowedOrigins);
        corsConfig.setExposedHeaders(exposedHeaders);
        corsConfig.setMaxAge(36000L);
        corsConfig.setAllowCredentials(true);

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);
        log.debug("跨域拦截处理启动成功允许条件为:/**");
        return new CorsFilter(source);
    }
}

虽然类上有@Order注解,且设置了优先级最高(值最小),可是这个注解在这里的作用是这个类的加载优先级,而不是CorsFilter过滤器的优先级,CorsFilter的优先级是默认的。

这里提供一种正确的设置方式,Spring Boot为了过滤器的优先级等设置,提供了FilterRegistrationBean这个类。

@Configuration
public class CorsConfig {
    private static Log log = LogFactory.getLog(CorsConfig.class);

    @Value("${eframework.cors.allowed.origins:*}")
    private String origins;

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildCorsConfig());
        FilterRegistrationBean<CorsFilter> filterRegistrationBean = new FilterRegistrationBean<>(new CorsFilter(source));
        filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return filterRegistrationBean;
    }

    private CorsConfiguration buildCorsConfig() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        List<String> exposedHeaders = Arrays.asList("x-auth-token", "content-type", "X-Requested-With", "XMLHttpRequest");
        List<String> allowedOrigins = Arrays.asList(StringUtils.split(origins, ","));

        List<String> allowedHeaders = Arrays.asList("*");
        List<String> allowedMethods = Arrays.asList("*");
        corsConfig.setAllowedHeaders(allowedHeaders);
        corsConfig.setAllowedMethods(allowedMethods);
        corsConfig.setAllowedOrigins(allowedOrigins);
        corsConfig.setExposedHeaders(exposedHeaders);
        corsConfig.setMaxAge(36000L);
        corsConfig.setAllowCredentials(true);
        return corsConfig;
    }
}

在这段代码中,filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE)设置了优先级最高,没有配置name、urlPatterns属性,使用默认值,针对所有请求("/*")进行过滤。

更改前的过滤器列表:

更改后的过滤器列表:

相关内容

之前在另一篇文章请求路径不对,预检请求preflight返回404,导致真实请求返回CORS错误里,也有一个浏览器提示了CORS,真正原因却不是的案例,原因其实都是类似的。

服务端返回内容跨域CORS之后,也在chrome/edge浏览器里显示出响应信息

Spring Boot下的过滤器优先级设置,以后再写一篇文章专门来说,@Order不一定生效,是有坑的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CORS过滤器优先级不对,预检请求正常,正式请求报错CORS跨域 的相关文章

随机推荐