4
votes

I setup a web application with Spring 3 and Resteasy; since my resources require authentication I am not allowed to use * as Access-Control-Allow-Origin. So I configured

org.jboss.resteasy.plugins.interceptors.CorsFilter

with the right origin domain. This works with a desktop client (Paw for Mac Os and others), but not with the browser (Chrome); the problem is that the response contains a double value for Access-Control-Allow-Origin, that is the one I configured and '*'.

CorsFilter is not to blame because, even if you configure more than one origin, it always puts just one value for the header, the one which the request asked for.

I simply have no idea on who's putting that extra (and wrong) header, any idea on where I could look for? Please note that the double header occurs on GET requests but not on OPTIONS requests.

4
Could you put together the smallest possible project that reproduces the problem, and post it on Github or something.Paul Samsotha

4 Answers

1
votes

I'm not sure where your doubled header comes from, but did you try to use custom filter? e.g. :

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class SimpleCorsFilter implements Filter {

public SimpleCorsFilter() {
}

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

        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }

}
0
votes

I finally found out there is a proprietary MessageBodyWriterInterceptor in the classpath which does a wrong add header; now it's on me to remove that. One thing I learned is that if something happens only when there is a body to write, a good starting point is surely the rendering pipeline

0
votes

I've tried the following actions and it worked as a charm:

First, register the CorsFilter provider class in your web.xml:

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>org.jboss.resteasy.plugins.interceptors.CorsFilter</param-value>
</context-param>

By doing so, your server is already enabled to handle CORS requests, however, you need to add some allowed origins to get it working, therefore, you should get access to the CorsFilter's instance, which was created by RestEasy then add all the URLs you wish to grant access to or add a * if you wish to grant access to any.

In this regard, if you're using RestEasy Spring Integration, you'll need to grab an instance of the org.jboss.resteasy.spi.ResteasyProviderFactory class by autowiring it into your code:

@Autowired
private ResteasyProviderFactory processor;

then use a setup method annotated with @PostConstruct to get the instance of the CorsFilter from the ResteasyProviderFactory like the code snippet below:

@PostConstruct
public void setUp() {
    ContainerRequestFilter[] requestFilters = processor.getContainerRequestFilterRegistry().preMatch();
    CorsFilter filter = (CorsFilter) Iterables.getLast(Iterables.filter(Arrays.asList(requestFilters), Predicates.instanceOf(CorsFilter.class)));
    filter.getAllowedOrigins().add("*");
}

P.S.: I'm using this frameworks:

  • Spring Framework 3.2.18.RELEASE
  • RestEasy 3.0.12.Final

I hope it helps!

0
votes

For those struggling like me who don't use the Application starter but only Spring + Reasteasy.

Just add in web.xml:

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>package.to.your.cors.CORSFilter</param-value>
</context-param>

And create the Java Class CORSFilter like

package package.to.your.cors;

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class CORSFilter implements ContainerResponseFilter {

    @Override
    public void filter(final ContainerRequestContext requestContext,
                       final ContainerResponseContext cres) throws IOException {
        cres.getHeaders().add("Access-Control-Allow-Origin", "*");
        cres.getHeaders().add("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE");
        cres.getHeaders().add("Access-Control-Allow-Headers", "X-Auth-Token, Content-Type");
        cres.getHeaders().add("Access-Control-Max-Age", "4800");
    }

}

It works like a charm.

PS: inspired by s_bighead answer but I could not comment his answer to add my details.