【问题标题】:Spring Boot CORS filter - CORS preflight channel did not succeedSpring Boot CORS 过滤器 - CORS 预检通道未成功
【发布时间】:2016-08-17 00:16:41
【问题描述】:

我需要将 CORS 过滤器添加到我的 Spring Boot Web 应用程序中。

我已添加 CORS 映射,如以下文档 http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cors.html 中所述

这是我的配置:

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // @formatter:off   
        registry
            .addMapping("/**")
            .allowedOrigins(CrossOrigin.DEFAULT_ORIGINS)
            .allowedHeaders(CrossOrigin.DEFAULT_ALLOWED_HEADERS)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600L);
        // @formatter:on
    }

...

}

现在当我尝试访问我的 API 时收到以下错误:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://example.com/api/v1.0/user. (Reason: CORS preflight channel did not succeed).

这是FF控制台的截图:

我做错了什么以及如何正确配置 CORS 标头以避免此问题?

【问题讨论】:

    标签: spring spring-boot spring-mvc spring-security cors


    【解决方案1】:

    我已通过创建新的 CORS 过滤器解决了这个问题:

    @Component
    public class CorsFilter extends OncePerRequestFilter {
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
            response.setHeader("Access-Control-Allow-Origin", "*");
            response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
            response.setHeader("Access-Control-Max-Age", "3600");
            response.setHeader("Access-Control-Allow-Headers", "authorization, content-type, xsrf-token");
            response.addHeader("Access-Control-Expose-Headers", "xsrf-token");
            if ("OPTIONS".equals(request.getMethod())) {
                response.setStatus(HttpServletResponse.SC_OK);
            } else { 
                filterChain.doFilter(request, response);
            }
        }
    }
    

    并将其添加到安全配置中:

    .addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class)
    

    更新 - 现在我改用更现代的方式:

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
    
            http
                .cors()
            .and()
    
            ...
        }
    
        @Bean
        public CorsConfigurationSource corsConfigurationSource() {
            CorsConfiguration configuration = new CorsConfiguration();
            configuration.setAllowedOrigins(Arrays.asList("*"));
            configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"));
            configuration.setAllowedHeaders(Arrays.asList("authorization", "content-type", "x-auth-token"));
            configuration.setExposedHeaders(Arrays.asList("x-auth-token"));
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", configuration);
            return source;
        }
    
    }
    

    【讨论】:

    • 如果您想支持适当的 RESTful API,您不应该在其中添加 PUT 动词吗?
    • 我也使用了上面的示例代码和其他代码,但它们都没有在应用程序级别上工作。最终,我将 CORS 设置为控制器级别。
    • 我们的项目不是 MVC,您未更新的解决方案有所帮助。
    • @alexanoid 最后一个使用 CorsConfigurationSource 的选项是否允许从例如数据库中动态加载允许的域?用例:一个管理员网络应用程序,您可以在其中控制您的应用程序允许的域。
    • 这是唯一可以在 AngularJs 1.6 中使用 $http.get 的配置,经过数小时的尝试,我发现了这个,但不知道为什么
    【解决方案2】:

    在让 CORS 与 spring data rest 一起工作时遇到同样的问题,这是我使用的过滤器代码。

        /**
     * Until url{https://jira.spring.io/browse/DATAREST-573} is fixed
     * 
     * @return
     */
    @Bean
    public CorsFilter corsFilter() {
    
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        //config.setAllowCredentials(true); // you USUALLY want this
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
    

    【讨论】:

    • 这是最好的配置,因为它不仅适用于控制器,也适用于拦截器!谢谢!
    • 你知道,我喜欢 Spring,但 CORS 配置一直让我伤心欲绝。每次我升级 Springboot 版本时,我的 REST 应用程序的 CORS 都会再次损坏。我的过滤器没有被捡起。它不在顶部。如果您包含弹簧数据,则不起作用。总有东西。在最后一轮中,这有助于我检索 oauth 令牌,但我仍然需要保留一个过滤器来处理 OPTIONS 飞行前:if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); }
    • 对不起,你把上面的代码放在哪里了?还是我需要创建一个新的类过滤器并在新类中创建上面的 Bean?
    • 代码被放置在任何具有@Configuration 注释的类中,但是使用 Spring Boot 2 有更简单的方法,请参阅页面下方的 cmets
    【解决方案3】:

    这非常简单并且运行良好。在您为 Web 安全配置编写的课程中,输入此行 httpSecury.cors();

    
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    
        @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
    
             httpSecurity.cors();     //  This enables cors
    
            // Your codes
    
        }
    
    }
    
    
    

    【讨论】:

      【解决方案4】:

      遵循两个教程后,我仍然遇到 CORS 错误:

      首先我遵循了网络安全指南:https://spring.io/guides/gs/securing-web/#scratch

      我第二次遵循 CORS 指南:https://spring.io/guides/gs/rest-service-cors/#global-cors-configuration

      要在遵循这些指南后解决我的问题,我必须将 http.cors() 添加到 http 安全性。

      @Override
      protected void configure(HttpSecurity http) throws Exception {
        http.cors()
        .and()
          ...
      } 
      

      添加.cors() 允许它使用我为我的CORS 配置声明的@Bean

      @Bean
      public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
          @Override
          public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("http://localhost:4200");
          }
        };
      }
      

      【讨论】:

      • 各地的文档和论坛帖子都给出了相同的程序,但没有人提到在void configure(HttpSecurity http) 实现中调用http.cors() 的要求。我真的很感激,因为经过长时间的斗争,这最终解决了我的问题。这实际上记录在某处吗?
      • 我必须在课堂上使用 @EnableWebSecurity 注释来执行此操作,该注释扩展了 WebSecurityConfigurerAdapter。 (我想这可能是其他一些人直接实现WebSecurityConfigurer<WebSecurity> 接口)
      【解决方案5】:

      对于它的价值,以下组合解决方案对我有用:

      1.

      @Configuration
      public class CorsConfiguration {
      
      //This can be used in combination with @CrossOrigin on the controller & method.
      
          @Bean
          public WebMvcConfigurer corsConfigurer() {
              return new WebMvcConfigurerAdapter() {
                  @Override
                  public void addCorsMappings(CorsRegistry registry) {
                      registry.addMapping("/**")
                              .allowedMethods("HEAD","OPTIONS")
                              .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "Accept");
                  }
              };
          }
      }
      

      2。 @CrossOrigin 在 RestController 类上。拥有@CrossOrigin 会读取@RequestMapping 注释和其中的HTTP 方法。其余请求因 CORS 错误而被拒绝。

      但是如果你想在你的项目中使用 Spring Security,那么你将无法使用上述解决方案。

      我使用的是 Spring Boot 版本 1.5.4.RELEASE。

      【讨论】:

        【解决方案6】:

        如果使用以下代码的 spring-boot 2 足以解决 cors 问题和 preflight 问题

        @Override
            public void configure(WebSecurity web) throws Exception {
        //      web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
                web.ignoring().antMatchers("/resources/**", "/index.html", "/login.html", "/partials/**", "/template/**", "/",
                        "/error/**", "/h2-console", "*/h2-console/*");
            }
        
            @Bean
            CorsConfigurationSource corsConfigurationSource() {
                CorsConfiguration config = new CorsConfiguration();
                config.applyPermitDefaultValues();
                config.setAllowCredentials(true);// this line is important it sends only specified domain instead of *
                UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                source.registerCorsConfiguration("/**", config);
                return source;
            }
        

        【讨论】:

          【解决方案7】:

          这对我有用

          @Configuration
          public class CorsConfig implements WebMvcConfigurer {
          
              public void addCorsMappings(CorsRegistry registry) {
                  registry.addMapping("/**")
                      .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS");
              }
          }
          

          【讨论】:

            【解决方案8】:

            如果您将 CORS 与 Spring Security 一起使用,这是最新的文档:https://docs.spring.io/spring-security/site/docs/current/reference/html5/#cors

            这类似于本页其他地方引用的代码:

            @EnableWebSecurity
            public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
            
                @Override
                protected void configure(HttpSecurity http) throws Exception {
                    http
                        // by default uses a Bean by the name of corsConfigurationSource
                        .cors(withDefaults())
                        ...
                }
            
                @Bean
                CorsConfigurationSource corsConfigurationSource() {
                    CorsConfiguration configuration = new CorsConfiguration();
                    configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
                    configuration.setAllowedMethods(Arrays.asList("GET","POST"));
                    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    source.registerCorsConfiguration("/**", configuration);
                    return source;
                }
            }
            

            尽管您可以在其他地方配置 CORS,但将其作为安全配置的一部分进行是有意义的,因为它们紧密相关,因为 CORS 处理必须在安全处理之前进行 - 之前的帖子已经指出.上面引用的文档中给出的原因是:

            "CORS 必须在 Spring Security 之前处理,因为 pre-flight 请求不会包含任何 cookie(即 JSESSIONID)。如果请求不包含任何 cookie 并且 Spring Security 在前,则请求将确定用户不是经过身份验证(因为请求中没有 cookie)并拒绝它。”

            在 http 配置的开头添加 .cors() 行 - 如上所示 - 实现了这一点。否则,飞行前 OPTIONS 请求将无响应。

            【讨论】:

              【解决方案9】:

              正确处理飞行前 OPTIONS 请求是必要的,但不足以使跨站点资源请求正常工作。

              在 OPTIONS 请求返回满意的标头后,对同一 URL 的任何后续请求的所有响应也必须具有必要的“Access-Control-Allow-Origin”标头,否则浏览器会吞下它们,他们赢了甚至不会出现在调试器窗口中。 https://stackoverflow.com/a/11951532/5649869

              【讨论】:

              • 一开始只在允许的方法中留下需要的http方法。然后尝试使用 curl curl -H "Origin: http://127.0.0.1" --verbose http://127.0.0.1/api/v1.0/user 发出原始请求。您必须在响应中看到 Access-Control-Allowed-* 标头。最后,尝试在 AllowedOrigin 中指定您的主机(127.0.0.1 或其他)。
              • 我试过allowedHeaders("xsrf-token").exposedHeaders("xsrf-token"),但还是不行。我想我需要这样的东西 - if ("OPTIONS".equals(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } 但我不知道如何将此逻辑添加到 CorsRegistry registry
              【解决方案10】:

              目前推荐的做CORS的方式是

              @Configuration
              @EnableWebMvc
              public class WebConfig implements WebMvcConfigurer {
              
                  @Override
                  public void addCorsMappings(CorsRegistry registry) {
              
                      registry.addMapping("/api/**")
                          .allowedOrigins("http://domain2.com")
                          .allowedMethods("PUT", "DELETE")
                          .allowedHeaders("header1", "header2", "header3")
                          .exposedHeaders("header1", "header2")
                          .allowCredentials(true).maxAge(3600);
              
                      // Add more mappings...
                  }
              }
              

              这是基于https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-cors

              但您还需要确保在您的 WebSecurityConfig 文件中启用了 CORS 并禁用了 CSRF。

              我曾经遇到过一个问题,即我的所有 POST 方法都不起作用(返回 403 禁止),而 GET 方法工作得很好,但是在禁用 CSRF 后问题就解决了

              【讨论】:

                【解决方案11】:

                第 1 步:在控制器中添加此注解

                @CrossOrigin
                public class JobController {}
                

                第 2 步:将其添加到您的任何配置中

                @Bean
                public WebMvcConfigurer corsConfigurer() {
                    return new WebMvcConfigurer() {
                        @Override
                        public void addCorsMappings(CorsRegistry registry) {
                            registry.addMapping("/**").allowedOrigins("*");
                        }
                    };
                }
                

                只有当你的控制器上有 @CrossOrigin 注释时这才有效

                【讨论】:

                  【解决方案12】:

                  这是我用于 Cors 配置以与 Spring Boot 配合使用的一段代码。它是主应用程序类中的 corsFilter 配置。

                  应用程序正在“http://localhost:4200”上运行

                  import org.springframework.context.annotation.Bean;
                  import org.springframework.web.cors.CorsConfiguration;
                  import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
                  import org.springframework.web.filter.CorsFilter;
                  
                  import java.util.Arrays;
                  
                  
                      @Bean
                      public CorsFilter corsFilter() {
                          CorsConfiguration corsConfiguration = new CorsConfiguration();
                          corsConfiguration.setAllowCredentials(true);
                          corsConfiguration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
                          corsConfiguration.setAllowedHeaders(Arrays.asList("Origin", "Access-Control-Allow-Origin", "Content-Type",
                                  "Accept", "Authorization", "Origin, Accept", "X-Requested-With",
                                  "Access-Control-Request-Method", "Access-Control-Request-Headers"));
                          corsConfiguration.setExposedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization",
                                  "Access-Control-Allow-Origin", "Access-Control-Allow-Origin", "Access-Control-Allow-Credentials"));
                          corsConfiguration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
                          UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
                          urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
                          return new CorsFilter(urlBasedCorsConfigurationSource);
                      }
                  
                  
                  

                  【讨论】:

                    【解决方案13】:

                    AFAIK,对于所有 http 请求,都会向服务器发送一个预检请求,以检查该特定 api 请求的访问。预检请求通常是一个“OPTION”http 请求,它发送即将到来的请求所需的元数据。

                    所以错误,预检通道不成功意味着发送到服务器的预检请求被阻止或拒绝。 在大多数情况下,这是因为

                    • “OPTION”请求不在 spring 安全配置允许的方法中
                    • spring security 中不允许使用 UI 的来源

                    Spring security 5.4.5开始,我们基本上可以让上面提到的几点来检查这是否是底层问题。

                    创建或更新扩展WebMvcConfigurer的类

                    @Override
                    public void addCorsMappings(CorsRegistry registry) {
                        //The pattern, allowedOrigins and allowedMethods should be restricted to the frontend application url,
                        //so that CORS attacks won't happen
                        registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
                    }
                    

                    这里,addMapping 带有一个“API 端点”的参数,我们提供“*”来配置服务器支持的所有端点。

                    allowedOrigins 对应于我们上面提供的映射支持的 UI 应用程序路径 (*)

                    allowedMethods 接收服务器允许的所有 http 方法的数组。

                    在生产环境中,我们提供的此配置应限制为适当的值。

                    另外,在您的扩展WebSecurityConfigurerAdapter

                    的配置类中
                    @Override
                    protected void configure(HttpSecurity http) throws Exception {
                        http.cors()
                                .and()
                                .......
                                .authenticated();
                    }
                    

                    注意我们提供的“http.cors()”方法

                    【讨论】:

                      猜你喜欢
                      • 2015-11-22
                      • 2015-10-15
                      • 2019-03-17
                      • 2016-08-06
                      • 2015-12-01
                      • 1970-01-01
                      • 2017-11-01
                      • 2015-05-26
                      • 1970-01-01
                      相关资源
                      最近更新 更多