JAX-RS: CORS

Introduction

CXF 2.5.1 introduces the initial support for the Cross-Origin Resource Sharing specification that "defines a mechanism to enable client-side cross-origin requests".

This Mozilla.org page provides a very good explanation of CORS.

Please see the package.html for a good introduction to CORS and the way it is supported in CXF JAX-RS.

Note that the CORS filter uses the JAX-RS selection algorithm to ensure that the JAX-RS resource method capable of handling the request does exist.

Maven dependencies

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-security-cors</artifactId>
  <version>2.6.1</version>
</dependency>

Examples

Here is the test code showing how CrossOriginResourceSharing annotations can be applied at the resource and individual method levels.

Note that an origin is restricted to "http://area51.mil:31415" by the 'allowOrigins' property, which may contain multiple URI values. A boolean 'allowAllOrigins' property can be used instead (to simplify the testing or when it is deemed it is secure enough within a given environment to allow for all the origins).

@CrossOriginResourceSharing(
        allowOrigins = {
           "http://area51.mil:31415"
        }, 
        allowCredentials = true, 
        maxAge = 1, 
        allowHeaders = {
           "X-custom-1", "X-custom-2"
        }, 
        exposeHeaders = {
           "X-custom-3", "X-custom-4"
        }
)
public class AnnotatedCorsServer {
    @Context
    private HttpHeaders headers;

    @GET
    @Produces("text/plain")
    @Path("/simpleGet/{echo}")
    public String simpleGet(@PathParam("echo") String echo) {
        return echo;
    }
    
    @POST
    @Produces("application/json")
    @Consumes("application/json")
    @Path("/unannotatedPost")
    public Response postSomething() {
        return Response.ok().build();
    }

    @DELETE
    @Path("/delete")
    public Response deleteSomething() {
        return Response.ok().build();
    }

    // This method will do a preflight check itself
    @OPTIONS
    @Path("/")
    @LocalPreflight
    public Response options() {
        String origin = headers.getRequestHeader("Origin").get(0);
        if ("http://area51.mil:3333".equals(origin)) {
            return Response.ok()
                           .header(CorsHeaderConstants.HEADER_AC_ALLOW_METHODS, "DELETE PUT")
                           .header(CorsHeaderConstants.HEADER_AC_ALLOW_CREDENTIALS, "false")
                           .header(CorsHeaderConstants.HEADER_AC_ALLOW_ORIGIN, "http://area51.mil:3333")
                           .build();
        } else {
            return Response.ok().build();
        }
    }

    @GET
    @CrossOriginResourceSharing(
         allowOrigins = { "http://area51.mil:31415" }, 
         allowCredentials = true, 
         exposeHeaders = { "X-custom-3", "X-custom-4" }
    )
    @Produces("text/plain")
    @Path("/annotatedGet/{echo}")
    public String annotatedGet(@PathParam("echo") String echo) {
        return echo;
    }

    /**
     * A method annotated to test preflight.
     * 
     * @param input
     * @return
     */
    @PUT
    @Consumes("text/plain")
    @Produces("text/plain")
    @Path("/annotatedPut")
    public String annotatedPut(String input) {
        return input;
    }
}

The server configuration fragment:


<beans>
        <bean id="cors-filter" class="org.apache.cxf.rs.security.cors.CrossOriginResourceSharingFilter"/>

	<jaxrs:server id="service" address="/rest">
		<jaxrs:serviceBeans>
			<ref bean="cors-server" />
		</jaxrs:serviceBeans>
		<jaxrs:providers>
			<ref bean="cors-filter" />
		</jaxrs:providers>
	</jaxrs:server>

        <bean id="cors-server" scope="prototype" 
	      class="org.apache.cxf.systest.jaxrs.cors.AnnotatedCorsServer" /> 

</beans>