Forum Stats

  • 3,827,668 Users
  • 2,260,802 Discussions
  • 7,897,328 Comments

Discussions

How can I get past CORS preflight request while using JAX-RS for standalone Jetty server?

e73319b1-be4d-469a-bdd5-c275da822bfd
edited Jan 25, 2017 6:57PM in Java Programming

I am trying to get a standalone Jetty server running with a web client. However I am running into issues with the preflight requests that are being sent before the actual request. When I make a request using Postman, I get the expected response (preflight request are not used in Postman), but when the request is made from Chrome I get the following error message:

     "XMLHttpRequest cannot load http://my.server.ip:8000/hello/api/test. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access."

The header I get back is as follows:

     "HTTP/1.1 200 OK Date: Wed, 26 Oct 2016 14:51:06 GMT Allow: POST,OPTIONS Date: Wed, 26 Oct 2016 14:51:06 GMT Content-Length: 0 Server: Jetty(9.2.10.v20150310)"

My relevant code is shown below. Any help solving this would be greatly appreciated as I have already spent several days trying to figure this out. If there is a way to fix this while still using JAXRSServerFactoryBean that would be great.

Thanks.

My Standalone server class is below:

    package com.my.path;

    import java.util.EnumSet;

    import java.util.HashMap;

    import java.util.Map;

    import javax.jws.WebService;

    import javax.servlet.DispatcherType;

    import javax.xml.stream.XMLInputFactory;

    import javax.xml.stream.XMLOutputFactory;

    import org.apache.cxf.binding.BindingFactoryManager;

    import org.apache.cxf.endpoint.Server;

    import org.apache.cxf.jaxrs.JAXRSBindingFactory;

    import org.apache.cxf.jaxrs.JAXRSServerFactoryBean;

    import org.codehaus.jettison.mapped.MappedXMLInputFactory;

    import org.codehaus.jettison.mapped.MappedXMLOutputFactory;

    import org.eclipse.jetty.server.handler.DefaultHandler;

    import org.eclipse.jetty.server.handler.HandlerList;

    import org.eclipse.jetty.servlet.FilterHolder;

    import org.eclipse.jetty.servlet.ServletContextHandler;

    import org.eclipse.jetty.servlets.CrossOriginFilter;

    public class myStandaloneServer {

       private static Server server = null;

       private static HelloWorldImpl module = null;

     

              .

              .

              .

     private static void publishService() throws FaultException {

  try {

  // Build the module for use in the web service

  module = new HelloWorldImpl();

  HelloWorld implementor = (HelloWorld) module;

  System.out.println("Starting Server");

         String address = "http://10.1.10.2:8000/hello";

  

         JAXRSServerFactoryBean svrFactory = new JAXRSServerFactoryBean();

  svrFactory.setAddress(address);

  svrFactory.setServiceBean(implementor);

  svrFactory.setBindingId(JAXRSBindingFactory.JAXRS_BINDING_ID);

  BindingFactoryManager manager = svrFactory.getBus().getExtension(BindingFactoryManager.class);

  JAXRSBindingFactory factory = new JAXRSBindingFactory();

  manager.registerBindingFactory(JAXRSBindingFactory.JAXRS_BINDING_ID, factory);

  factory.setBus(svrFactory.getBus());

  Map<String, Object> properties = new HashMap<String, Object>();

  // Don't prefix namespaces...

  HashMap<String, String> nstojns = new HashMap<String, String>();

  nstojns.put("http://mywebsite.com/", "si");

  nstojns.put("http://schemas.xmlsoap.org/soap/envelope/", "");

  MappedXMLInputFactory xif = new MappedXMLInputFactory(nstojns);

  properties.put(XMLInputFactory.class.getName(), xif);

  MappedXMLOutputFactory xof = new MappedXMLOutputFactory(nstojns);

  properties.put(XMLOutputFactory.class.getName(), xof);

  properties.put("Content-Type", "application/json");

  properties.put("Access-Control-Allow-Origin", "*");

  properties.put("Access-Control-Allow-Headers", "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers");

  svrFactory.setProperties(properties);

  ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);

  context.setContextPath("/hello");

  context.setResourceBase(".");

  FilterHolder holder =  new FilterHolder (new CrossOriginFilter());

  holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");

  holder.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,content-type,access-control-allow-origin,access-control-allow-methods,access-control-allow-headers");

     holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,PUT,POST,DELETE,OPTIONS");

  holder.setName("cross-origin");

  //FilterMapping fm = new FilterMapping();

  //fm.setFilterName("cross-origin");

  //fm.setPathSpec("*");

  //context.addFilter(holder, fm );

  context.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

  HandlerList handlers = new HandlerList();

     handlers.addHandler(context);

     handlers.addHandler(new DefaultHandler());

  // svrFactory.setHandler(handlers);

  server = svrFactory.create();

        

        

  } catch (Exception e) {

  e.printStackTrace();

  logError("Error" + e);

  getServerLogger().printStackTrace(e);

  }

  }

  

My webs services interface is below:

    package com.my.path;

    import javax.jws.WebMethod;

    import javax.jws.WebService;

    import javax.ws.rs.FormParam;

    import javax.ws.rs.GET;

    import javax.ws.rs.OPTIONS;

    import javax.ws.rs.POST;

    import javax.ws.rs.Path;

    import javax.ws.rs.Produces;

    import javax.ws.rs.core.Response;

    import javax.xml.bind.annotation.XmlSeeAlso;

    import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;

    import org.apache.cxf.rs.security.cors.LocalPreflight;

    import com.my.otherPath.data.FaultException;

    @CrossOriginResourceSharing(

        allowOrigins = {

           "*"

        },

        allowCredentials = true,

        maxAge = 1,

        allowHeaders = {

        "X-Requested-With", "content-type", "access-control-allow-origin", "access-control-allow-methods", "access-control-allow-headers"

        }

    )

    @Path("/api/")

    @WebService

    @XmlSeeAlso({ com.my.otherPath.data.DateRangeSearchParamItem.class })

    public interface HelloWorld {

    @GET

    @POST

    @Produces("text/xml")

    @Path("/test")

    @CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")

    @WebMethod(operationName = "sayHi", exclude = false)

    Response sayHi(@FormParam("name") String name);

    @OPTIONS

    @CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")

    @Path("/*")

    @LocalPreflight

    @WebMethod(operationName = "options", exclude = false)

    public abstract Response options() throws FaultException;

    }

Here is my implementation:

    package com.my.path;

    import javax.jws.WebMethod;

    import javax.jws.WebParam;

    import javax.jws.WebService;

    import javax.ws.rs.OPTIONS;

    import javax.ws.rs.POST;

    import javax.ws.rs.Path;

    import javax.ws.rs.Produces;

    import javax.ws.rs.core.Response;

    import org.apache.cxf.rs.security.cors.CrossOriginResourceSharing;

    import org.apache.cxf.rs.security.cors.LocalPreflight;

    @Path("/api/")

    @WebService(endpointInterface = "com.my.path.HelloWorld", serviceName = "HelloWorld")

    public class HelloWorldImpl implements HelloWorld {

    @POST

    @Produces("text/xml")

    @Path("/test/")

    @CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")

    @WebMethod(operationName = "sayhHi", exclude = false)

       public Response sayHi(@WebParam(targetNamespace = "http://com.mySite/", name="name") String name) {

           System.out.println("sayHi called");

       

           return Response.ok()

  .header("Access-Control-Allow-Origin", "*")

  .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")

  .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")

  .allow("OPTIONS")

  .entity("Hello " + name)

  .build();    

       }

    @OPTIONS

    @CrossOriginResourceSharing(allowAllOrigins = true, allowOrigins = "*", allowHeaders = "X-Requested-With, content-type, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers")

    @Path("/")

    @LocalPreflight

    @WebMethod(operationName = "options", exclude = false)

    public Response options() {

        System.out.println("prflight endpoint");

        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")

             .header("Access-Control-Max-Age", "1209600")

             .allow("OPTIONS")

             .build();

    }

    }

And here is my web.xml:

    <?xml version="1.0" encoding="UTF-8"?>

  <web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <display-name>CSA</display-name>

  <welcome-file-list>

  <welcome-file>index.html</welcome-file>

  <welcome-file>index.htm</welcome-file>

  <welcome-file>index.jsp</welcome-file>

  <welcome-file>default.html</welcome-file>

  <welcome-file>default.htm</welcome-file>

  <welcome-file>default.jsp</welcome-file>

  </welcome-file-list>

   

     <servlet>

         <description>Apache CXF Endpoint</description>

         <display-name>CXF Servlet</display-name>

      <servlet-name>CXFServlet</servlet-name>

      <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>

      <load-on-startup>1</load-on-startup>

    </servlet>

  

    <servlet-mapping>

    <servlet-name>CXFServlet</servlet-name>

      <url-pattern>/services/*</url-pattern>

    </servlet-mapping>

  

    

    <!--  <servlet>

         <description>JAX-RS Tools Generated - Do not modify</description>

         <servlet-name>javax.ws.rs.core.Application</servlet-name>

         <servlet-class>javax.ws.rs.core.Application</servlet-class>

         <load-on-startup>1</load-on-startup>

     </servlet>

     <servlet-mapping>

         <servlet-name>javax.ws.rs.core.Application</servlet-name>

         <url-pattern>/jaxrs/*</url-pattern>

     </servlet-mapping> -->

    

    <!-- Common JAX-RS Servlet Definition -->

    <!--   <servlet>

         <servlet-name>javax.ws.rs.core.Application</servlet-name>

         <servlet-class>javax.ws.rs.core.Application</servlet-class>

         <load-on-startup>1</load-on-startup>

     </servlet>

     <servlet-mapping>

         <servlet-name>javax.ws.rs.core.Application</servlet-name>

         <url-pattern>/*</url-pattern>

     </servlet-mapping> -->

    

    

  

  

  <!-- This filter is needed for the Access-Control-Allow-Origin header to work. -->   

  <filter>

    <filter-name>cross-origin</filter-name>

    <filter-class>org.eclipse.jetty.servlets.CrossOriginFilter</filter-class>

    <init-param>

        <param-name>allowedOrigins</param-name>

        <param-value>*</param-value>

    </init-param>

    <init-param>

        <param-name>allowedMethods</param-name>

          <param-value>GET,POST,OPTIONS,DELETE,PUT</param-value>

    </init-param>

    <init-param>

        <param-name>allowedHeaders</param-name>

        <param-value>origin, content-type, accept, authorization, X-Requested-With, access-control-allow-origin, access-control-allow-methods, access-control-allow-headers</param-value>

    </init-param>

  </filter>

  <filter-mapping>

      <filter-name>cross-origin</filter-name>

      <url-pattern>/*</url-pattern>

  </filter-mapping>

  <filter>

  <filter-name>CORSFilter</filter-name>

  <filter-class>CORSFilter</filter-class>

  </filter>

  <filter-mapping>

  <filter-name>CORSFilter</filter-name>

  <url-pattern>/api/*</url-pattern>

  </filter-mapping>

  

  

  

  <!-- This filter is needed for WS clients that can't handle status code 500. It changes the status code to 200. -->   

  <filter>

     <filter-name>ExceptionFilter</filter-name>

     <filter-class>csa.core.ws.ExceptionFilter</filter-class>

  </filter>

  <filter-mapping>

     <filter-name>ExceptionFilter</filter-name>

  <servlet-name>cxf</servlet-name>

  </filter-mapping>  

  

  

  <session-config>

     <session-timeout>60</session-timeout>

  </session-config>

  </web-app>

Tagged:
eudriscabrera-JavaNet

Answers

  • cesarHernandezGT
    cesarHernandezGT Member Posts: 42
    edited Jan 25, 2017 6:57PM

    If you resolved the issue, be welcome to update this thread so everybody can benefit from it.

    If you still have the issue, this are some notes that I have on this matter:

    Option 1:

    You can create a ContainerResponseFilter. check this video for instructions: https://youtu.be/nDW6DQSNrIY

    Option 2:

    Instead of the filter add the Access-Control-Allow-Origin to all the methods and don't forge to implement the OPTIONS request with something like the one describe in this stackOverflow link:

    <span class="lit" style="color: #7d2727;">@OPTIONS</span><span class="pln" style="color: #303336;"><br/></span><span class="lit" style="color: #7d2727;">@Path</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"{path : .*}"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/></span><span class="kwd" style="color: #101094;">public</span><span class="pln" style="color: #303336;"> </span><span class="typ" style="color: #2b91af;">Response</span><span class="pln" style="color: #303336;"> options</span><span class="pun" style="color: #303336;">()</span><span class="pln" style="color: #303336;"> </span><span class="pun" style="color: #303336;">{</span><span class="pln" style="color: #303336;"><br/>   </span><span class="kwd" style="color: #101094;">return</span><span class="pln" style="color: #303336;"> </span><span class="typ" style="color: #2b91af;">Response</span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">ok</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">""</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">header</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"Access-Control-Allow-Origin"</span><span class="pun" style="color: #303336;">,</span><span class="pln" style="color: #303336;"> </span><span class="str" style="color: #7d2727;">"*"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">header</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"Access-Control-Allow-Headers"</span><span class="pun" style="color: #303336;">,</span><span class="pln" style="color: #303336;"> </span><span class="str" style="color: #7d2727;">"origin, content-type, accept, authorization"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">header</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"Access-Control-Allow-Credentials"</span><span class="pun" style="color: #303336;">,</span><span class="pln" style="color: #303336;"> </span><span class="str" style="color: #7d2727;">"true"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">header</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"Access-Control-Allow-Methods"</span><span class="pun" style="color: #303336;">,</span><span class="pln" style="color: #303336;"> </span><span class="str" style="color: #7d2727;">"GET, POST, PUT, DELETE, OPTIONS, HEAD"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">header</span><span class="pun" style="color: #303336;">(</span><span class="str" style="color: #7d2727;">"Access-Control-Max-Age"</span><span class="pun" style="color: #303336;">,</span><span class="pln" style="color: #303336;"> </span><span class="str" style="color: #7d2727;">"1209600"</span><span class="pun" style="color: #303336;">)</span><span class="pln" style="color: #303336;"><br/>   </span><span class="pun" style="color: #303336;">.</span><span class="pln" style="color: #303336;">build</span><span class="pun" style="color: #303336;">();</span><span class="pln" style="color: #303336;"><br/></span><span class="pun" style="color: #303336;">}</span>

    Option 3:

    Since you are using cxf, checkout: Apache CXF -- JAX-RS CORS

    General notes:

    1) Keep in mind that if you do for instance a PUT, the preflight check will be trigger automatically with a OPTIONS request firsts and then the PUT request.

    2) For development test I highly recomend to set the Access-Control-Max-Age to 1 second and prevent the browser to cache the OPTIONS request.

    Glad to know if this was helpful !

    eudriscabrera-JavaNet
This discussion has been closed.