【问题标题】:CORS preflight request fails due to a standard header由于标准标头,CORS 预检请求失败
【发布时间】:2016-11-25 05:33:15
【问题描述】:

在调试我遇到的 CORS 问题时,我发现了以下行为。 Chrome 发出以下 OPTIONS 预检请求(由 Chrome 自己用 CURL 重写):

curl -v 'https://www.example.com/api/v1/users' -X OPTIONS -H 'Access-Control-Request-Method: POST' -H 'Origin: http://example.com' -H 'Accept-Encoding: gzip,deflate,sdch' -H 'Accept-Language: es-ES,es;q=0.8,en;q=0.6' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36' -H 'Accept: */*' -H 'Referer: http://example.com/users/new' -H 'Connection: keep-alive' -H 'Access-Control-Request-Headers: accept, x-api-key, content-type'

如果满足以下条件,则服务器对此请求的响应:

< HTTP/1.1 403 Forbidden
< Date: Thu, 21 Jul 2016 14:16:56 GMT
* Server Apache/2.4.7 (Ubuntu) is not blacklisted
< Server: Apache/2.4.7 (Ubuntu)
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: SAMEORIGIN
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
< Content-Length: 20
< Keep-Alive: timeout=5, max=100
< Connection: Keep-Alive

作为响应“无效 CORS 请求”的主体。如果我重复删除标头“Access-Control-Request-Method”(并且仅该标头)的请求,则 OPTIONS 请求会成功并具有以下响应:

< HTTP/1.1 200 OK
< Date: Thu, 21 Jul 2016 14:21:27 GMT
* Server Apache/2.4.7 (Ubuntu) is not blacklisted
< Server: Apache/2.4.7 (Ubuntu)
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< Strict-Transport-Security: max-age=31536000 ; includeSubDomains
< X-Frame-Options: SAMEORIGIN 
< Access-Control-Allow-Headers: origin, content-type, accept, x-requested-with, x-api-key
< Access-Control-Max-Age: 60
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
< Access-Control-Allow-Origin: *
< Allow: GET, HEAD, POST, PUT, DELETE, TRACE, OPTIONS, PATCH
< Content-Length: 0
< Keep-Alive: timeout=5, max=100
< Connection: Keep-Alive

但是,有问题的标头是CORS spec standard header,所以它不应该阻止请求成功,对吧?为什么这个标头会导致这种行为?

当使用 Chrome 发出请求时,如何调整服务器发送的访问控制标头以使请求正常工作?

顺便说一句,我使用的是 Chrome 36.0,服务器使用的是 Spring Boot,CORS 标头由 Spring 管理。

当 Firefox (v47.0) 发出请求时,行为不同,但结果类似。 Firefox 甚至不发送预检请求,它直接发送 POST 请求,该请求收到 403 Forbidden 作为响应。但是,如果我使用“复制为 cURL”选项复制请求,并从终端窗口重复它,它会成功并在响应中发送正确的 CORS 标头。

有什么想法吗?

更新:Firefox 确实发送了预检 OPTIONS 请求(如 Live HTTP 标头插件所示),但 Firebug 将其屏蔽,因此两种浏览器中的行为完全相同。在这两种浏览器中,“Access-control-request-method”标头是导致请求失败的区别。

【问题讨论】:

    标签: spring cors


    【解决方案1】:

    经过一番苦苦挣扎,我终于找到了问题所在。我在 Spring 中配置了一个请求映射来处理 OPTIONS 流量,如下所示:

    @RequestMapping(value= "/api/**", method=RequestMethod.OPTIONS)
    public void corsHeaders(HttpServletResponse response) {
        response.addHeader("Access-Control-Allow-Origin", "*");
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        response.addHeader("Access-Control-Allow-Headers", "origin, content-type, accept, x-requested-with");
        response.addHeader("Access-Control-Max-Age", "3600");
    }
    

    我不知道默认情况下 Spring 使用default CORS processor,它似乎干扰了我的请求映射。删除我的请求映射并将@CrossOrigin 注释添加到适当的请求映射解决了这个问题。

    【讨论】:

    • 是的,真是个大旅行,Spring 有一个默认的 cors 处理器,但除非它被配置,否则如果你在 Apache 中设置它,它实际上会中断正常的 CORS 处理。看起来你要么必须配置一个 CorsFilter,要么按照这里的建议 - spring.io/guides/gs/rest-service-cors
    • 我发现这篇文章也很有帮助:stackoverflow.com/questions/9521690/… DispatchServlet 必须配置为传递选项请求,否则它永远不会到达映射请求:... yourServlet org.springframework.web.servlet.DispatcherServletdispatchOptionsRequesttrue 1 ...
    • 我已经通过这篇文章解决了,请看下面的链接。 spring.io/blog/2015/06/08/cors-support-in-spring-framework
    【解决方案2】:

    我也遇到了同样的问题,并找到了在 spring boot 中启用全局 cors 问题的解决方案

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                    .allowedHeaders("*");
        }
    }
    

    在此之后,我们还需要在 spring 安全级别启用 CORS,所以为此 在您的 SecurityConfiguration 类中添加 cors() 以扩展 WebSecurityConfigurerAdapter

     @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
    
            httpSecurity
                    .cors()
                    .and()
                    .csrf().disable()
                    .authorizeRequests()..
    
        }
    

    【讨论】:

      【解决方案3】:

      我有同样的问题。我已经通过在我的 Spring MVC 配置中添加“OPTIONS”到允许的 CORS 方法来解决它。

      @Configuration
      @EnableWebMvc
      @ComponentScan
      public class RestApiServletConfig extends WebMvcConfigurerAdapter {
      
          @Override
          public void addCorsMappings(CorsRegistry registry) {
              super.addCorsMappings(registry);
              registry.addMapping("/**")
                      .allowedOrigins("http://localhost:3000", "http://localhost:8080")
                      .allowedMethods("GET", "PUT", "POST", "DELETE", "OPTIONS");
          }
      }
      

      【讨论】:

        【解决方案4】:

        对我来说,我在每个控制器 api 调用中添加了 @crossorigin 注释。

        @CrossOrigin
        @PostMapping(path = "/getListOfIndividuals", produces = { "application/json" }, consumes = { "application/json" })
        public ResponseEntity<String> test(@RequestBody String viewIndividualModel)
                throws Exception {
            String individualDetails = globalService.getIndividualDetails(viewIndividualModel);
        
        
            finalString = discSpecAssmentService.getViewFormForDisciplineEvaluation( viewIndividualModel);
        
            return new ResponseEntity<String>(finalString, HttpStatus.OK);
        }
        

        【讨论】:

          【解决方案5】:

          编辑:在安全配置中启用 CORS 并确保选项请求绕过安全

          @Override
          protected void configure(HttpSecurity httpSecurity) throws Exception {
              httpSecurity
                      .cors()
                      .and()
                      .authorizeRequests()
                      .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                      // More security configuration here
          }
          

          【讨论】:

          • 注意 - Spring 的文档明确规定:“由于 CORS 请求是自动分派的,因此您无需更改 DispatcherServlet dispatchOptionsRequest 初始化参数值;建议使用其默认值 (false)。”
          • @Itaypk 你是对的,没有必要改变 dispatchOptionsRequest
          【解决方案6】:

          我将此添加为答案,因为我无法将其格式化为投票最多的答案。

          我发现这篇文章也很有帮助:How to handle HTTP OPTIONS with Spring MVC?

          DispatchServlet 必须配置为传递选项请求,否则它永远不会到达映射的请求:

          ...
            <servlet>
              <servlet-name>yourServlet</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <init-param>
                <param-name>dispatchOptionsRequest</param-name>
                <param-value>true</param-value>
              </init-param>
              <load-on-startup>1</load-on-startup>
            </servlet>
          ...
          

          【讨论】:

            【解决方案7】:

            我在使用test-cors.org 网站在我们的端点上测试 CORS 时确实遇到了这个问题,它表现出与上述完全相同的行为。

            我所做的方法是使用全局 CORS 过滤器,而不是使用 @CrossOrigin 注释。

            @Configuration
            class CorsConfig : WebMvcConfigurer {
                override fun addCorsMappings(registry: CorsRegistry) {
                    registry.addMapping("/**")
                            .allowCredentials(true)
                            .allowedHeaders("*")
                            .allowedMethods("*")
                            .allowedOrigins("*")
                            .maxAge(3600)
                }
            }
            

            请注意,您不应该使用@EnableWebMvc,除非您想控制Spring Boot Auto-configuration,如here...这可能会导致一些“问题”,如herehere所述

            需要下一个自定义配置(解决方案部分来自 here),否则您将遇到特定的 CORS 飞行前问题:

            @Configuration
            class CustomWebSecurityConfigurerAdapter : WebSecurityConfigurerAdapter() {
                override fun configure(http: HttpSecurity) {
                    http.cors().and().csrf().disable()
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2018-03-20
              • 2015-11-24
              • 2020-02-21
              • 2023-01-08
              • 2016-12-28
              • 2013-05-18
              • 1970-01-01
              • 2016-03-08
              • 2016-03-03
              相关资源
              最近更新 更多