Spring Boot CORS 过滤器 - CORS 预检请求失败

48

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

我已经按照以下文档描述的方式添加了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头以避免此问题?

13个回答

113

我通过创建一个新的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;
    }

}

1
如果你想支持合适的RESTful API,难道不应该在其中添加PUT动词吗? - Marco Tedone
1
我使用了上面的示例代码和其他代码,但是它们都没有在应用程序级别上起作用。最终,我在控制器级别上设置了CORS。 - Nadeem Jamali
1
这是唯一能与AngularJs 1.6中的$http.get配合使用的配置,经过数小时的尝试后,我才发现这一点,之前完全不知道为什么。 - Bằng Rikimaru
1
CorsFilter与addFilterBefore对我很有用。谢谢。在尝试了3个小时后,您的解决方案对我起作用了。 - biniam
已经存在一个org.springframework.web.filter.CorsFilter...不需要编写自己的。 - undefined
显示剩余3条评论

20

我在尝试使用Spring Data REST时也遇到了CORS的问题,以下是我使用的过滤器代码。

    /**
 * 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);
}

1
这是最佳配置,因为它不仅适用于控制器,还适用于拦截器!谢谢! - Maciek Kreft
1
你知道吗,我喜欢Spring,但CORS配置一直让我苦恼不已。每次我升级我的Springboot版本,CORS就会再次破坏我的REST应用程序。我的过滤器没有被拾取。它不在顶部。如果包含spring-data,则无法正常工作。总是有些问题。对于最后一轮,这帮助我检索我的oauth令牌,但我仍然必须保持一个过滤器来处理“OPTIONS”预检请求:if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); } - Glenn
抱歉,以上代码应该放在哪里?还是需要创建一个新的过滤器类并在新类中创建上述Bean? - Henry
代码放置在任何带有@Configuration注解的类中,但是使用Spring Boot 2有更简单的方法,详见页面下方的评论。 - Romell

7

这非常简单且运行良好。在您编写的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

    }

}



5
在按照这两篇教程后,我仍然遇到了CORS错误:
首先我按照Web安全指南操作: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配置的@Bean中添加.cors()使其可以使用。
@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()的要求。我非常感激,因为经过长时间的奋斗,这最终解决了我的问题。这个要求实际上有记录吗? - rineez
我必须在我的类内部使用@EnableWebSecurity注解来完成这个任务,此注解继承自WebSecurityConfigurerAdapter。(我猜对于其他一些人来说,这可能是WebSecurityConfigurer<WebSecurity>接口的直接实现) - rineez

4
如果你正在使用Spring Security的CORS,这是最新的文档链接: 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处理必须发生在安全处理之前,这是以前的帖子所指出的。上述引用文档中给出的原因是:

“必须在Spring Security之前处理CORS,因为预检请求不会包含任何Cookie(即JSESSIONID)。如果请求不包含任何Cookie并且Spring Security第一个处理,那么请求将确定用户未经身份验证(因为请求中没有Cookie),并将其拒绝。”

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


3

这对我有用

@Configuration
public class CorsConfig implements WebMvcConfigurer {

    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS");
    }
}

如果您正在使用 spring-boot-starter-web,这似乎是配置它最简单的方法。请注意在生产环境中进一步注明 .allowedOrigins.allowedHeaders - seedme

3
就我个人而言,以下组合解决方案适用于我:
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. 在RestController类上使用@CrossOrigin。使用@CrossOrigin读取@RequestMapping注解和其中的HTTP方法。其余请求将被拒绝并显示CORS错误。

但是,如果您想在项目中使用Spring Security,则以上解决方案将无法奏效。

我正在使用Spring Boot版本1.5.4.RELEASE。


3
这是我用于与Spring Boot一起使用Cors Configurations的代码片段。它是主应用程序类中的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);
    }



1

目前推荐的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 forbidden),而GET方法却可以正常工作,但在禁用CSRF后问题得到解决。


1
如果使用spring-boot 2,以下代码足以解决cors问题和预检请求问题。
@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;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接