Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CORS issues with Spring 4.2.0.RC2 and Tomcat 8.0.23

Before I was using Spring 4.1.x release and had my own Cors Filter and Interceptor. I was also using Tomcat 8.0.8 all was good.

As I had moved the same application to Tomcat 8.0.23 CORS stopped working.

Therefore, I had the application updated to the following versions of Spring dependencies as of my POM:

org.springframework: 4.2.0.RC2
org.springframework.security: 4.0.1.RELEASE
org.springframework.security.oauth2: 2.0.7.RELEASE
org.springframework.ws.version: 2.2.0.RELEASE
spring.data.commons.core.version: 1.4.1.RELEASE

Now, I had also updated my securityConfiguration.xml to have the proper schemas for security 4.0.1.RELEASE.

The application works fine, all changes perfectly except for CORS.

So I had followed the instructions and added to my WebMvcConfigurerAdapter implementation class the following:

@Override
    public void addCorsMappings(CorsRegistry registry) {
        Properties prop = null;

        try {

            InputStream in = getClass().getResourceAsStream(
                    "/com/nando/config/cors.properties");
            prop = new Properties();
            prop.load(in);
            in.close();            
        } catch (IOException e) {
            prop = null;
        }

        if (prop != null) {
            String domains = prop.getProperty("allowed.origins");

            List<String> allowedOrigins = new ArrayList<String>(
                    Arrays.asList(domains.split(",")));

            if (allowedOrigins.size() > 0) {
                String[] arrayOrigins = null;
                arrayOrigins = new String[allowedOrigins.size()];
                arrayOrigins = allowedOrigins.toArray(arrayOrigins);
                registry.addMapping("/**")
                .allowedOrigins(arrayOrigins)
                .allowedMethods("PUT, POST, GET, OPTIONS, DELETE")
                .allowedHeaders("*")
                .exposedHeaders("Authorization", "Content-Type")
                .allowCredentials(false).maxAge(3600);
            }
        } else {
            registry.addMapping("/**")
            .allowedOrigins("*")
            .allowedMethods("PUT, POST, GET, OPTIONS, DELETE")
            .allowedHeaders("*")
            .exposedHeaders("Authorization", "Content-Type")
            .allowCredentials(false).maxAge(3600);
        }
    }

Just doing that alone does not fix the problem, although using a debugger to Tomcat I can see that being properly executed and added. I have a properties file so that I can change allowed domains without recompiling the application as I had done before with my own implementation on 4.1.x Spring.

Because I had this application as an OAuth2 application I had implemented an interceptor too.

On my WebMvcConfigurerAdapter implementation I had an interceptor before, so I had tried to add it to it as so:

@Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new CORSInterceptor());
        super.addInterceptors(registry);
    }

Which loads my CORSInterceptor implemention as follows:

package com.nando.api.filters;

import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

/**
 * 
 * @author Nando
 *
 */
@Component
public class CORSInterceptor extends HandlerInterceptorAdapter {

    private Properties prop = new Properties();

    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {

        InputStream in = getClass().getResourceAsStream("/com/nando/config/cors.properties");
        prop.load(in);
        in.close();

        String domains = prop.getProperty("allowed.origins");

        Set<String> allowedOrigins = new HashSet<String>(Arrays.asList(domains
                .split(",")));

        String originHeader = "";
        originHeader = request.getHeader("Origin");

        if (null != originHeader) {
            for (String s : allowedOrigins) {
                if (originHeader.contains(s)) {
                    if (!response.getHeaderNames().contains("Access-Control-Allow-Origin")) {
                        response.addHeader("Access-Control-Allow-Origin",
                                originHeader);
                    }
                    if (!response.getHeaderNames().contains("Access-Control-Allow-Methods")) {
                        response.addHeader("Access-Control-Allow-Methods",
                                "OPTIONS, GET, POST, PUT, DELETE, HEAD");
                    }
                    if (!response.getHeaderNames().contains("Access-Control-Allow-Credentials")) {
                        response.addHeader("Access-Control-Allow-Credentials", "true");
                    }
                    if (!response.getHeaderNames().contains("Access-Control-Allow-Headers")) {
                        response.addHeader("Access-Control-Allow-Headers",
                            "Authorization, Origin, Content-Type, x-Requested-with, Accept, Accept-Enconding, X-CSRF-Token");
                    }
if (!response.getHeaderNames().contains("Access-Control-Max-Age")) {
 response.addHeader("Access-Control-Max-Age", "1800");
                    }
                    return true;
                }
            }
        }
        return true;
    }
}

And still will not make a difference.

I do not want to add the CORS filter to Tomcat as it will not allow me to set the domains on a properties file, forcing me to edit the Tomcat instances configuration every time a domain is added or removed.

Looks like I have a conundrum here.

Also I thought that by following Spring 4.2 directions on setting up CORS would make it easier which is not the case.

Can anyone help?

Thanks,

Carlos.

like image 571
Carlos Antunes Avatar asked Jul 07 '15 22:07

Carlos Antunes


1 Answers

If you want to use Spring CORS support, I think you should :

  • Change allowedMethods("PUT, POST, GET, OPTIONS, DELETE") to allowedMethods("PUT", "POST", "GET", "OPTIONS", "DELETE") (it is a varargs parameter)
  • Remove allowedHeaders("*"), it is already the defaults
  • Remove your CORSInterceptor
  • Remove Tomcat CORS Filter
  • Check that it works without Spring Security
  • As proposed by Brian, give us more details with the HTTP requests/responses

As an alternative, be aware that Spring Framework 4.2 GA also provides a CorsFilter implementation that could be used in combinaison with UrlBasedCorsConfigurationSource to provide global CORS support.

If you are using Spring Boot (which supports Filter beans), it could be something like this in one of your @Configuration annotated classes:

@Bean
public CorsFilter corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    config.addExposedHeader("Authorization");
    config.addExposedHeader("Content-Type");
    source.registerCorsConfiguration("/**", config);
    return new CorsFilter(source);
}
like image 198
Sébastien Deleuze Avatar answered Nov 08 '22 13:11

Sébastien Deleuze