Zuul Proxy CORS header contains multiple values, headers repeated twice - Java Spring Boot CORS filter config
Asked Answered
T

4

10

Why would I be getting every CORS header doubled? I am using the Zuul Proxy to have the request to a service proxied through an API gateway.

I must have something misconfigured with my spring security filtering order.

When I access a route that requires authentication I am getting an error like:

Request to service through API Gateway error

XMLHttpRequest cannot load https://myservice.mydomain.com:8095/service/v1/account/txHistory?acctId=0.  
The 'Access-Control-Allow-Origin' header contains multiple values '*, *', but only one is allowed. 
Origin 'http://localhost:9000' is therefore not allowed access.

Chrome network log

I checked the response in Chrome devtools and sure enough the CORS headers are repeated twice: CORS Headers repeated

So this looks like somehow my CORS filter is being called twice for each reply. I don't know why that would be happening at this point. It could be that my filter is added before the ChannelProcessingFilter.

Code for API Gateway CORS filter:

public class SimpleCORSFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.setHeader("Access-Control-Allow-Origin", "*");
        res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
        res.setHeader("Access-Control-Max-Age", "3600");
        res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, Accept, x-requested-with, Cache-Control");
        chain.doFilter(request, res);
    }

    @Override
    public void destroy() {}
}

My API Gateway security configuration:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Inject
    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    private UserDetailsService userDetailsService;

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(userDetailsService)
                .passwordEncoder(new BCryptPasswordEncoder());
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
           .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/health","/metrics", "/v1/users/register").permitAll()
                .antMatchers("/mappings", "/v1/**", "/service/**").authenticated()
                .and()
            .httpBasic()
                .realmName("apiRealm")
                .and()
            .csrf()
                .disable()
            .headers()
                .frameOptions().disable()
            .and().addFilterBefore(new SimpleCORSFilter(), ChannelProcessingFilter.class);
    }

}

I could solve this by checking if the header is null and then setting it only if it is empty or null, though that does not seem like the best solution. I would like to understand what I have done to cause the headers to be preset twice.

Tezel answered 16/3, 2016 at 17:30 Comment(0)
F
12

I had a similar problem but the issue was that I had CORS filter in both APIGateway and other services. IF thats not your case then try this CORS filter.

Add this to the class where you have @EnableZuulProxy in the API Gateway. This should do the trick i have a similar configuration on mine.

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    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);
}
Freighter answered 7/4, 2016 at 23:40 Comment(8)
I did end up having CORS in my API Gateway and one of my services. I solved it by having CORS only in my API Gateway. Thank you for posting a CorsFilter bean definition!Tezel
What if you can't/don't want to change the other services security configuration? I tried checking the header in the filter, but it always returns null even when there's the header set by the serviceDorindadorine
@Dorindadorine you might to play with the UrlBasedCorsConfigurationSource object to see that.... this configuration is for all since it says register to all URL's {source.registerCorsConfiguration("/**", config);} you can have specific urls where you want these configuration.Freighter
@GrinishNepal that would require a coded knowing dependency of what are the other services available in the cloud, where hiding this information is the main purpose of an API Gateway. I simply want a filter that dinamically add CORS support if not already supported on the service side (and doesn't break it when it is).Dorindadorine
It will be better if you create a question so that you can get the exact help you want.Freighter
Actually, this is exactly the same problem of mine, but with an accepted answer that doesn't change anything about the problem (The CorsFilter provided does just the same as the SimpleCORSFilter of OP). Even the OP said that "solved it by having CORS only in my API Gateway", otherwise the main problem of the question (duplicated headers) still persists.Dorindadorine
the duplication is due to having the filter in both services and API gateway which will happen if you have it at both places. the solution to the OP's question was to have it in one place only. So, i still have no idea what you are asking for.Freighter
Removing a feature from a problematic system isn't a solution to the problem. It's changing the environment until the problem vanish with no real solution adaptation to the situation. What I'm asking for is an answer to this question that does not rely on removing part of the problem in order to hide it undercover (what will happen tomorrow if I want to proxy an external services?).Dorindadorine
S
14

I also had the same issue, and i added the CorsFilter into the class where has @ EnableZuulProxy, but it still didn't solve my problem.

According to the github Q&A Zuul Access-Control-* Headers are duplicated

zuul.ignored-headers=Access-Control-Allow-Credentials, Access-Control-Allow-Origin

To add it to my zuul's bootstrap.properties, it works!!!

Selenography answered 19/7, 2018 at 9:41 Comment(1)
Absolutely a saver!!!Whelan
F
12

I had a similar problem but the issue was that I had CORS filter in both APIGateway and other services. IF thats not your case then try this CORS filter.

Add this to the class where you have @EnableZuulProxy in the API Gateway. This should do the trick i have a similar configuration on mine.

@Bean
public CorsFilter corsFilter() {
    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    final CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    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);
}
Freighter answered 7/4, 2016 at 23:40 Comment(8)
I did end up having CORS in my API Gateway and one of my services. I solved it by having CORS only in my API Gateway. Thank you for posting a CorsFilter bean definition!Tezel
What if you can't/don't want to change the other services security configuration? I tried checking the header in the filter, but it always returns null even when there's the header set by the serviceDorindadorine
@Dorindadorine you might to play with the UrlBasedCorsConfigurationSource object to see that.... this configuration is for all since it says register to all URL's {source.registerCorsConfiguration("/**", config);} you can have specific urls where you want these configuration.Freighter
@GrinishNepal that would require a coded knowing dependency of what are the other services available in the cloud, where hiding this information is the main purpose of an API Gateway. I simply want a filter that dinamically add CORS support if not already supported on the service side (and doesn't break it when it is).Dorindadorine
It will be better if you create a question so that you can get the exact help you want.Freighter
Actually, this is exactly the same problem of mine, but with an accepted answer that doesn't change anything about the problem (The CorsFilter provided does just the same as the SimpleCORSFilter of OP). Even the OP said that "solved it by having CORS only in my API Gateway", otherwise the main problem of the question (duplicated headers) still persists.Dorindadorine
the duplication is due to having the filter in both services and API gateway which will happen if you have it at both places. the solution to the OP's question was to have it in one place only. So, i still have no idea what you are asking for.Freighter
Removing a feature from a problematic system isn't a solution to the problem. It's changing the environment until the problem vanish with no real solution adaptation to the situation. What I'm asking for is an answer to this question that does not rely on removing part of the problem in order to hide it undercover (what will happen tomorrow if I want to proxy an external services?).Dorindadorine
S
0

For me this solution worked to solve CORS problem in zuul.

endpoints.cors.allowed-origins=*
endpoints.cors.allowed-headers=*
endpoints.cors.allowed-methods=*

However, this does not seem to work for me in one of my staging environment.

Substrate answered 19/6, 2019 at 5:49 Comment(0)
T
0

I fixed by adding filter into cloud gateway:

spring: cloud:
gateway: default-filters: - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials, RETAIN_UNIQUE

Thank to https://lifesaver.codes/answer/doubled-cors-headers-after-upgrade-to-greenwich-728

Tearing answered 28/3, 2022 at 10:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.