Spring Boot - CORS 过滤器适用于 GET,但不适用于其他 HTTP 动词

Posted

技术标签:

【中文标题】Spring Boot - CORS 过滤器适用于 GET,但不适用于其他 HTTP 动词【英文标题】:Spring Boot - CORS filter works for GET, but not for other HTTP Verbs 【发布时间】:2021-04-07 09:37:46 【问题描述】:

我正在使用带有 Angular 9 前端的 Spring Boot 2.2.5 应用程序。

我一直在尝试在 Spring Boot 后端配置一个 CORS 过滤器,以允许任何来源的任何请求的任何标头。

根据我的研究,我目前在我的主要 Application.java 文件中实现的内容应该可以工作:

@Bean
public CorsFilter corsFilter() 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("OPTIONS");
    config.addAllowedMethod("GET");
    config.addAllowedMethod("POST");
    config.addAllowedMethod("PUT");
    config.addAllowedMethod("DELETE");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);

但是,我所经历的是,这只允许我从前端到后端的控制器执行GET 调用。对POSTPUT 的任何其他调用都会失败并出现以下错误:

Access to XMLHttpRequest at 'http://localhost:8080/spring-boot-starter-seed/default-theme/save/cyan' from origin 'http://localhost:8083' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

我知道我走在了正确的轨道上,因为如果我删除了我实现的@Bean,那么从前端到后端的所有调用都会由于该错误而失败。但是在实现该 Bean 时,由于某种原因,它仅适用于 GET 请求。

有什么我遗漏的吗?

***** 更新 *****

我不知道这是否会有所帮助,但是,我正在使用 Azure AD 对此应用程序进行身份验证。我有一个看起来像这样的WebSecurityConfig

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() 
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
        grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http
                .authorizeRequests()
                .antMatchers("/actuator/**", "/heartbeat/**", "/register", "/unregister").permitAll()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt()
                .jwtAuthenticationConverter(this.jwtAuthenticationConverter());
    

在我之前的帖子中,我说过 GET 请求有效,但这似乎有些错误。

GET /heartbeat 有效。不过……

GET /default-theme 不起作用并失败并出现同样的 No Access-Control-Allow-Origin 错误。

这两个Controller路径的唯一区别是/default-theme不包含在排除的antMatchers中,它受到@PreAuthorize(value = "hasRole('ROLE_access')")的保护

【问题讨论】:

【参考方案1】:

我认为这里的问题是由于调度程序servlet的过滤器:

试试这个类来实现一个过滤器:

public class CORSFilter extends GenericFilterBean implements Filter 

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chaine)
            throws IOException, ServletException 
        HttpServletResponse httpResponse = (HttpServletResponse)response;
        httpResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpResponse.setHeader("Access-Control-Allow-Methods", "*");
        httpResponse.setHeader("Access-Control-Allow-Headers", "*");
        httpResponse.setHeader("Access-Control-Allow-Credentials", "false");
        httpResponse.setHeader("Access-Control-Max-Age", "3600");
        System.out.println("****************** CORS Configuration Completed *******************");
        chaine.doFilter(request, response);
    


然后将 bean 注入您的主应用程序类或任何地方:

@Bean
    public FilterRegistrationBean crosFilterRegistration()
        FilterRegistrationBean registrationBean = new FilterRegistrationBean(new CORSFilter());
        registrationBean.setName("CORS Filter");
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(1);
        return registrationBean;
    

希望这会有所帮助

【讨论】:

Amanoun,我尝试了您提供的内容,但结果相同。我已经用更多信息更新了我的帖子。【参考方案2】:

因此,事实证明,我对原始帖子的更新是正确的,假设 Spring Security 和我的 WebSecurityConfig 是原因的一部分。

我在here的答案中找到了真正的解决方案。

我需要先更新我的HttpSecurity 配置以包含.cors()

@Override
protected void configure(HttpSecurity http) throws Exception 
    http.cors().and()
            .authorizeRequests()
            .antMatchers("/actuator/**", "/heartbeat", "/register", "/unregister").permitAll()
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(this.jwtAuthenticationConverter());

完成此操作后,所有其他 CORS 配置都是不必要的,可以从我的应用程序中删除。只需将@CrossOrigin 注解添加到任何相关控制器即可。

有了这个,调用我的 GET /default-theme 控制器,受 @PreAuthorize(value = "hasRole('ROLE_access')") 保护,摆脱了 No Access-Control-Allow-Origin 错误,并导致了我预期的错误,即 401 响应。

所有这一切的根本原因是因为我的任务是将此应用程序从 LDAP 身份验证迁移到 Azure AD 身份验证。

希望这对将来的某人有所帮助。

【讨论】:

【参考方案3】:

WebSecurityConfig 类中,您需要配置cors,如下所示

@Override
protected void configure(HttpSecurity http) throws Exception 
    http
     ...
     .and()
     .cors().configurationSource(corsConfigurationSource())
     .and()
     ...

@Bean
    public CorsConfigurationSource corsConfigurationSource() 
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList(
                "list of domains here"
                )
        );
        configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT", "OPTIONS"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(
                Arrays.asList(
                        "Access-Control-Allow-Headers",
                        "Access-Control-Allow-Origin",
                        "Access-Control-Request-Method",
                        "Access-Control-Request-Headers",
                        "Origin", "Cache-Control",
                        "Content-Type",
                        "Authorization"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

【讨论】:

以上是关于Spring Boot - CORS 过滤器适用于 GET,但不适用于其他 HTTP 动词的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot CORS 通过配置启用

Zuul Proxy CORS 头包含多个值,头重复两次 - Java Spring Boot CORS 过滤器配置

Spring Boot CORS 过滤器不适用于 AngularJS

如何在 Spring Boot 安全性中禁用 CORS

如何在 Spring Boot 安全性中禁用 CORS

Spring Boot 中的 CORS 与 Spring Security + React