Spring Boot CORS过滤器 - CORS预检频道没有成功

时间:2016-04-23 10:23:00

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

我需要在我的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控制台的屏幕截图:

enter image description here

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

13 个答案:

答案 0 :(得分:41)

我已经通过创建新的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);
        }
    }
}

并将其添加到securty配置中:

.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 :(得分:6)

如果让CORS与spring数据休息一起工作有同样的问题,这就是我使用的过滤器代码。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

void main()
{
    char *words[] = {"sausage","blubber","pencil","cloud","moon","water","computer","school","network","hammer","walking","violently","mediocre","literature","chair","two","window","cords","musical","zebra","xylophone","penguin","home","dog","final","ink","teacher","fun","website","banana","uncle","softly","mega","ten","awesome","attatch","blue","internet","bottle","tight","zone","tomato","prison","hydro","cleaning","telivision","send","frog","cup","book","zooming","falling","evily","gamer","lid","juice","moniter","captain","bonding","loudly","thudding","guitar","shaving","hair","soccer","water","racket","table","late","media","desktop","flipper","club","flying","smooth","monster","purple","guardian","bold","hyperlink","presentation","world","national","comment","element","magic","lion","sand","crust","toast","jam","hunter","forest","foraging","silently","tawesomated","joshing","pong","RANDOM","WORD"};
    char *questions[] = {"sausage1","blubber1","pencil1","cloud1","moon1","water1","computer1","school1","network1","hammer1","walking1","violently1","mediocre1","literature1","chair1","two1","window1","cords1","musical1","zebra1","xylophone1","penguin1","home1","dog1","final1","ink1","teacher1","fun1","website1","banana1","uncle1","softly1","mega1","ten1","awesome1","attatch1","blue1","internet1","bottle1","tight1","zone1","tomato1","prison1","hydro1","cleaning1","telivision1","send1","frog1","cup1","book1","zooming1","falling1","evily1","gamer1","lid1","juice1","moniter1","captain1","bonding1","loudly1","thudding1","guitar1","shaving1","hair1","soccer1","water1","racket1","table1","late1","media1","desktop1","flipper1","club1","flying1","smooth1","monster1","purple1","guardian1","bold1","hyperlink1","presentation1","world1","national1","comment1","element1","magic1","lion1","sand1","crust1","toast1","jam1","hunter1","forest1","foraging1","silently1","tawesomated1","joshing1","pong1","RANDOM1","WORD1"};
    char answer[255] = "";

    int word;
    int vec[20] = { 0 };
    int i, j;
    int x=0;
    srand(time(NULL));

    do{
        for (i = 0; i < 20; i++) {
            int okay = 0;

            while (!okay) {
                vec[i] = rand() % 100 + 1;
                okay = 1;

                for (j = 0; j < i; j++) {
                    if (vec[i] == vec[j]) okay = 0;
                }
            }

            word=vec[i];

            printf("%s\n",questions[word]); //print a word
            scanf("%255s",answer);// wait for the user to type the word
            printf("%s\n",answer[x]);// show what the user typed

            printf("%s\n\n",words[word]);// show how they should of typed it

        }
    }while (x<20,x++);

    return 0;
}

答案 2 :(得分:4)

按照以下两个教程操作,我仍然遇到CORS错误:

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

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

要按照这些指南解决问题,我必须在http安全性中添加http.cors()

@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");
    }
  };
}

答案 3 :(得分:2)

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

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,那么您将无法使用上述解决方案。

我使用的是春季启动版1.5.4.RELEASE。

答案 4 :(得分:1)

这非常简单并且运行良好。在您为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 :(得分:1)

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

@CrossOrigin
public class JobController {}

Step2:将其添加到您的任何配置中

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

这仅在您的控制器上具有@CrossOrigin注释时才有效

答案 6 :(得分:1)

如果您在 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 处理必须在安全处理之前进行 - 之前的帖子已经指出.上面引用的文档中给出的原因是:

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

在 http 配置的开头添加 .cors() 行 - 如上所示 - 可以实现。否则,飞行前 OPTIONS 请求将得不到响应。

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

目前推荐的做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 forbiden),而GET方法工作得很好,但这在CSRF被禁用后解决了

答案 9 :(得分:0)

如果使用下面的代码使用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;
    }

答案 10 :(得分:0)

这对我有用

@Configuration
public class CorsConfig implements WebMvcConfigurer {

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

答案 11 :(得分:0)

这是我用于 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);
    }


答案 12 :(得分:0)

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()”方法