78

I developed a set of restful web services. I couldn't call any of these methods from remote clients due to the error No 'Access-Control-Allow-Origin' header is present on the requested resource.

The services work perfectly on localhost. Is there any changes or configs to do on the server side to resolve the issue. i.e. to enable cross domain requests.

I'm using WildFly 8, JavaEE 7

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
Naili
  • 1,564
  • 3
  • 20
  • 27
  • 2
    You can learn more about CORS here: http://www.html5rocks.com/en/tutorials/cors/ You can learn how to add CORS support to JAX-RS here: http://cxf.apache.org/docs/jax-rs-cors.html – monsur May 03 '14 at 22:33
  • That's useful, but I was looking for a solution without modifying the clients code. – Naili May 03 '14 at 22:38
  • 1
    You question asks for changes to the server side. Most of the changes for CORS are done on the server-side. There are just minimal, if any, changes needed in the client. – monsur May 04 '14 at 01:53

10 Answers10

119

I was wondering the same thing, so after a bit of research I found that the easiest way was simply to use a JAX-RS ContainerResponseFilter to add the relevant CORS headers. This way you don't need to replace the whole web services stack with CXF (Wildfly uses CXF is some form, but it doesn't look like it uses it for JAX-RS maybe only JAX-WS).

Regardless if you use this filter it will add the headers to every REST webservice.

package com.yourdomain.package;

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-Headers", "origin, content-type, accept, authorization");
      cres.getHeaders().add("Access-Control-Allow-Credentials", "true");
      cres.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
      cres.getHeaders().add("Access-Control-Max-Age", "1209600");
   }

}

Then when I tested with curl, the response had the CORS headers:

$ curl -D - "http://localhost:8080/rest/test"
HTTP/1.1 200 OK
X-Powered-By: Undertow 1
Access-Control-Allow-Headers: origin, content-type, accept, authorization
Server: Wildfly 8
Date: Tue, 13 May 2014 12:30:00 GMT
Connection: keep-alive
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
Transfer-Encoding: chunked
Content-Type: application/json
Access-Control-Max-Age: 1209600
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS, HEAD

My understanding is that it's the @Provider annotation that tells the JAX-RS runtime to use the filter, without the annotation nothing happens.

I got the idea about using the ContainerResponseFilter from a Jersey example.

Joel Pearson
  • 1,622
  • 1
  • 16
  • 28
  • 8
    yes I used the same solution you posted here. But I believe that we re making a security issue by allowing every cross domain connection. – Naili May 13 '14 at 15:23
  • 1
    Yes that's true if your REST web services use cookies for securing them. But if you are simply trying to make some public REST web services then there is no security issue.You didn't mention in your question if you just needed 1 domain to work ie services.yourdomain for example or every domain. – Joel Pearson May 13 '14 at 23:28
  • If you're planning to add only a specific host instead of *, don't forget to add the port as well – ACV Apr 08 '15 at 20:40
  • @JoelPearson Could you please tell how to configure this filter ? Should I do it in web.xml ? – Jay Sep 09 '15 at 15:52
  • @Jay it uses the @ Provider annotation, so as long as you have CDI enabled for your ear/war then no extra configuration is required – Joel Pearson Sep 10 '15 at 22:54
  • +1 for @Provider annotation. I too was looking at examples online but somehow missed this critical part! – bradimus Oct 02 '15 at 16:53
  • I am running the services on a jetty server with jersey servlet container. In this case, only having the @Provider was not sufficient. The CORSFilter class had to be registered as a jersey servlet init parameter as so: `jerseyServlet.setInitParameter( "jersey.config.server.provider.classnames", MyService.class.getCanonicalName() + ", org.glassfish.jersey.media.multipart.MultiPartFeature, " + CORSFilter.class.getCanonicalName())` – Ashok Dec 01 '15 at 11:04
  • @Ashok this question was about Wildfly 8 and JEE 7, and not jetty/jersey, so if you have other questions you should create a different question, as this solution will not be relevant to you. – Joel Pearson Dec 02 '15 at 01:29
  • 1
    @Joel Pearson Agreed. I came across this question through a google search for "enable CORS in jax-rs". Just thought it might be helpful to others who land on this page similarly. Nevertheless, the accepted answer does not seem to suggest anything specific to wildfly or jee7. May be I am missing the link? This was the solution that worked for me on jetty+jersey and thanks for posting it. – Ashok Dec 02 '15 at 15:10
  • @Ashok true it is probably not JEE 7, looks like the Provider annotation was available in JEE 6: http://docs.oracle.com/javaee/6/api/index.html?javax/ws/rs/ext/Provider.html The thing that makes it Wildfly is the fact you need CDI for it to work as is. But it's possible that it would work in any JEE 6 container since it's not referencing Wildfly APIs directly. It is interesting that you can still get that working in JAX-RS outside JEE though. Interesting information, thanks. – Joel Pearson Dec 02 '15 at 23:42
  • 2
    `Access-Control-Allow-Credentials: true` this line is actually conflicting with `"Access-Control-Allow-Origin", "*"` as it would either be a security issue, or (at least in Chrome) is just denied. If you want to allow credentials, you need to specify a specific origin, not a wildcard. – Stijn de Witt Feb 02 '16 at 14:26
  • 1
    First get the request origin: `String origin = requestContext.getHeaderString("origin");`, then (when `origin !== null`), use that origin i.s.o a wildcard: `cres.getHeaders().add("Access-Control-Allow-Origin", origin);`. If you're API is using cookies for auth (e.g. when using container managed authentication), check the origin against a whitelist of allowed origins before setting the CORS headers. – Stijn de Witt Feb 15 '16 at 08:33
  • Thanks a lot men, for those wondering when to put this code, you just need this code on your org.yourdomain.rest package, if your using wildfly 10. – netto Oct 17 '16 at 06:48
  • 1
    We had the Problem, that the provider wasn't registered by default and the following question helped: https://stackoverflow.com/questions/32328012/jax-rs-not-registering-resource-with-provider-annotation – boutta Nov 23 '17 at 08:04
  • @Naili, there is only a security issue if your application restricts access based on IP number (which is a very bad practice), only in this case a malicious site could access data it couldn't otherwise retrieve. in all other circumstances, as credentials aren't allowed (as noted by Stijn de Witt) there is no security issue. – Reto Gmür Feb 27 '18 at 20:45
  • This worked for me when deploying against JBoss EAP 7.1 only if I explicitly added the CORSFilter instance in the JaxRsApplication singletons. `@Provider` on its own didn't work for me. – Marcus Junius Brutus Jan 29 '20 at 14:32
  • Can I remove/disable default response headers such as `Server: Wildfly 8`? – Ahmad Nadeem May 11 '20 at 10:56
  • This is insufficient. If there is authentication, you must also ensure it does not apply to OPTIONS requests, preferably by adding the `@OPTIONS @Path("{path : .*}")` handler per `nemesisfixx` answer. – Jan Hudec Aug 14 '20 at 10:15
22

I was facing a similar problem, and had tried to use @Alex Petty's solution, but apart from having to set the CORS headers on each JAX-RS endpoint in my class, as such:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}

I had to further define a catch-all OPTIONS endpoint that would return the CORS headers for any other OPTIONS request in the class, and thus catch all endpoint of the sort:

@OPTIONS
@Path("{path : .*}")
public Response options() {
    return Response.ok("")
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .build();
}

Only after doing this, could I properly use my JAX-RS API endpoints from Jquery Ajax clients on other domains or hosts.

Community
  • 1
  • 1
JWL
  • 13,591
  • 7
  • 57
  • 63
  • I am having a strange problem. The response header (.header("SOME_HEADER","SOME_HEADER") set by me is not getting rendered in swagger UI in Google chrome, Mozilla but in IE 11. I have checked that this response header is coming to browser (in console response header of developer tool) but the same is not getting rendered in Swagger UI. – Debaprasad Jana Dec 28 '15 at 10:27
  • That's not correct. You have to use filters. Use a Filter when you want to filter and/or modify requests based on specific conditions. Use a Servlet when you want to control, preprocess and/or postprocess requests. http://docs.oracle.com/javaee/6/tutorial/doc/bnagb.html – max_dev Jun 11 '16 at 07:48
  • I just added this **.header("Access-Control-Allow-Origin", "*")** and it worked – Pini Cheyni May 06 '17 at 11:20
13

I found an even easier (RestEasy-specific) way to enable CORS on Wildfly without using a filter and where you can control your APIs response header configuration at the resource level.

For example:

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getMemberList() {
    List<Member> memberList = memberDao.listMembers();
    members.addAll(memberList);
    return Response
            .status(200)
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Credentials", "true")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "1209600")
            .entity(memberList)
            .build();
}
Alex Petty
  • 423
  • 3
  • 6
  • Thank you. I was not able to get 'javax.ws.rs.container' classes (why? another JAX-RS version?), and your solution worked just fine. – Guildenstern70 Sep 12 '14 at 10:54
  • This way works only for GET, when trying with POST (from the client) the same error occurs, I am still trying to find out how to solve this issue, because my "origin" will vary all the time. – Francisco Souza Sep 10 '15 at 09:58
  • 1
    This will never work, its wrong. CORS first call the HTTP method "OPTIONS" before calling "GET", "POST" or whatever. The header entries will have to be defined within a "OPTIONS" method. If they are not, the browser wont accept it. HTTP request filtering is the best way for doing it. One other thing, if you're using a serious HTTP server, like Apache, the entry **"Access-Control-Allow-Origin", "*"** is to sparse, and the browser wont allow it. You have to specifically add the authorizing DNS, e.g. "**Access-Control-Allow-Origin: my.domain.com**". – jbrios777 Aug 10 '16 at 01:12
  • But if you have 100s of methods... a better approach would be to use a filter – ACV Aug 30 '16 at 20:44
  • A complete answer with proper explanation to pre-flight requests has been given by @Paul Samsotha and here is the link - https://stackoverflow.com/a/28067653/3257644 – Mahesh Feb 17 '20 at 04:48
11

I have had good luck configuring Cross-origin resource sharing (CORS) for my API (on Wildfly) by using this lib:

<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.1</version>
</dependency>

It's very easy to setup. Just add the above dependency to your pom and then add the following config to the webapp section of your web.xml file.

<filter>
    <filter-name>CORS</filter-name>
    <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>

    <init-param>
        <param-name>cors.allowGenericHttpRequests</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowOrigin</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.allowSubdomains</param-name>
        <param-value>false</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedMethods</param-name>
        <param-value>GET, HEAD, POST, DELETE, OPTIONS</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportedHeaders</param-name>
        <param-value>*</param-value>
    </init-param>

    <init-param>
        <param-name>cors.supportsCredentials</param-name>
        <param-value>true</param-value>
    </init-param>

    <init-param>
        <param-name>cors.maxAge</param-name>
        <param-value>3600</param-value>
    </init-param>

</filter>

<filter-mapping>
    <!-- CORS Filter mapping -->
    <filter-name>CORS</filter-name>
    <url-pattern>*</url-pattern>
</filter-mapping>

You can also configure it with a properties file instead if you prefer. This lib works like a charm and gives you a lot of configuration flexibility!

Alex Petty
  • 423
  • 3
  • 6
  • I wanted to try this but I have this error on my log console : `The 'Access-Control-Allow-Origin' header contains multiple values 'http://127.0.0.1, *', but only one is allowed.` An idea what to change on the web.xml to make it right ? – Ayyoub Jul 22 '15 at 15:00
  • 2
    I tried this, but can't see this added to the header. – Jay Sep 09 '15 at 15:47
9

None of the other answers worked for me, but this did:

import javax.ws.rs.core.Response;

Then change the return type of the service method to Response, and change the return statement to:

return Response.ok(resp).header("Access-Control-Allow-Origin", "*").build();

Where resp is the original response object.

Alex R
  • 11,364
  • 15
  • 100
  • 180
  • This only works when it's a simple request. That is if the request is GET or POST and request headers are simple(The only simple headers are Accept, Accept-Language, Content-Language, Content-Type= application/x-www-form-urlencoded, multipart/form-data, text/plain). Otherwise browser will send a OPTIONS request prior to the actual request. – Ranuka Dec 03 '16 at 14:11
4

You can also implement javax.ws.rs.core.Feature as below to implement CORS.

import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import javax.ws.rs.ext.Provider;
import org.jboss.resteasy.plugins.interceptors.CorsFilter;

@Provider
 public class CorsFeature implements Feature {

  @Override
  public boolean configure(FeatureContext context) {
    CorsFilter corsFilter = new CorsFilter();
    corsFilter.getAllowedOrigins().add("*");
    context.register(corsFilter);
    return true;
 }

}
Ranuka
  • 773
  • 7
  • 13
3

The solution for this adds some header on the response. In spring or spring boot have annotations, but in the older system, there are maybe no annotations. You can solve with adding a filter.

Filter class:

package com.koushik.restapis.intercepter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;


public class RestCorsFilter implements Filter {

    @Override
    public void destroy() {
    enter code here
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
    
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.addHeader("Access-Control-Allow-Origin", "*");
        resp.addHeader("Access-Control-Allow-Headers", "*");
        resp.addHeader("Access-Control-Allow-Methods", "*");
        
        chain.doFilter(request, resp);
        
    }

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

}

web.xml

  <filter>
    <filter-name>RestCorsFilter</filter-name>
    <filter-class>com.koushik.restapis.RestCorsFilter</filter-class>
  </filter>
    <filter-mapping>
    <filter-name>RestCorsFilter</filter-name>
    <url-pattern>/apis/*</url-pattern>
  </filter-mapping>
404Koushik
  • 31
  • 3
0

Just to add something to other responses. Allowing * is a little bit dangerous. What can be done is configuring a database of the allowed origin (it can be a file)

Then when the request arrive you can do:

// this will return you the origin 
String[] referers = requestContext.getHeaders().get("referer")

// then search in your DB if the origin is allowed
if (referers != null && referers.lenght == 1 && isAllowedOriging(referers[0])) {
        containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", referers[0]);
        containerResponseContext.getHeaders().add("Access-Control-Allow-Headers", "origin, content-type, accept, authorization, <HERE PUT YOUR DEDICATED HEADERS>);
        containerResponseContext.getHeaders().add("Access-Control-Allow-Credentials", "true");
        containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        containerResponseContext.getHeaders().add("Access-Control-Max-Age", "1209600");
}

In that way you won't allow everyone.

Roger Ng
  • 771
  • 11
  • 28
Geoffrey
  • 1,151
  • 3
  • 13
  • 26
0

@Joel Pearson answer helped but for someone who is new to JAX-RS like me and is running the service on tomcat by configuring web.xml should be careful while creating the class and putting it anywhere in the project. See the package you specified in under for jersey and create this filter class there. That way it worked for me.

Kodin
  • 772
  • 8
  • 23
0

I had this problem recently and solved it but basically create a filter that implements ContainerResponseFilter and add the CORS headers as below:

@Override
    public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
        containerResponseContext.getHeaders().add(
                "Access-Control-Allow-Origin", "*");
        containerResponseContext.getHeaders().add(
                "Access-Control-Allow-Credentials", "true");
        containerResponseContext.getHeaders().add(
                "Access-Control-Allow-Headers",
                "origin, content-type, accept, authorization");
        containerResponseContext.getHeaders().add(
                "Access-Control-Allow-Methods",
                "GET, POST, PUT, DELETE, OPTIONS, HEAD");
    }

Since you're using JAX-RS, your application class extends the Applicaton class and you just need to override its getSingletons method:

@Override
public Set<Object> getSingletons() {
        return mySingletons();
    }

where your singletons could be as below:

public static Set<Object> mySingletons() {
        Set<Object> singletons = new HashSet<>();
        singletons.add(new MyFilter());
        return singletons;
    }

I tried the above answers with the changes on web.xml etc and none of them worked so I came up with this solution, having tried everything.

Rinor
  • 1
  • 1