Vamos a indicar los pasos para realizar la configuración de CORS en nuestros servicios Rest mediante anotaciones.
Lo primero que necesitamos es añadir la siguiente configuración en el fichero web.xml.
<context-param> <param-name>com.sun.jersey.spi.container.ResourceFilters</param-name> <param-value>es.um.atica.jersey.cors.ResourceCorsFilterFactory</param-value> </context-param>
Recordemos que el parámetro de contexto com.sun.jersey.spi.container.ResourceFilters se puede indicar el valor como una lista separada por ','. Por lo que si ya existe dicha declaración solo hay que añadir el es.um.atica.jersey.cors.ResourceCorsFilterFactory.
Ahora en los métodos de los servicios tenemos que utilizar la anotacion @CrossDomain que se puede indicar a nivel de clase o a nivel de método y tenemos los siguientes escenarios:
La anotacion @CrossDomain tiene las siguientes porpiedades:
Por motivos de seguridad, no es apropiado utilizar el valor “*” en la propiedad origins.
Primer ejemplo de petición CORS simple, donde en la anotación @CrossDomain indicamos:
@CrossDomain(methods = RequestMethod.GET, origins = "https://www.mydomain.com") @Path("/simple") @GET @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } ) public Response simpleCorsTest() { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); }
Ejemplo de CORS con preflight o autorización previa, donde en la anotación @CrossDomain indicamos:
@CrossDomain(methods = { RequestMethod.GET, RequestMethod.OPTIONS }, origins = "https://www.mydomain.com") @Path("/preflighted") @GET @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } ) public Response preflightedCorsTest() { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); }
Ahora además debemos añadir el método para gestionar el preflight. Para eso tenemos que indicar el método HTTP OPTIONS con el mismo @Path. Añadimos la anotación @CrossDomainOptions, para indicar que es un OPTIONS de CORS. La lógica interna recupera el método asociado para el @Path y recupera la información de @CrossDomain para preparar las respuesta. Este método realmente nunca se ejecuta, pero por la limitación de la API JAXRS de no podemos poner más de un método HTTP en el mismo método JAVA.
@CrossDomainOptions @Path("/preflighted") @OPTIONS public Response preflightedCorsTestCorsOptions() { return null; }
Ejemplo de CORS con preflight o autorización previa, donde tenemos varios métodos con el mismo valor de @Path. En este caso para gestionar bien el preflight, debemos indicar en la propiedad method de la anotación @CrossDomainOptions el nombre del método correcto.
@CrossDomain(methods = { RequestMethod.GET, RequestMethod.OPTIONS }, origins = "https://www.mydomain.com") @Path("/preflighted/{param}") @GET @Produces( MediaType.APPLICATION_JSON ) public Response preflightedCorsTest2(@QueryParam("param") String param) { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); } @CrossDomain(methods = { RequestMethod.GET, RequestMethod.OPTIONS }, origins = "https://www.mydomain.com") @Path("/preflighted/{param}") @GET @Produces( MediaType.APPLICATION_XML ) public Response preflightedCorsTest3(@QueryParam("param") Integer param) { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); }
Configuración para el preflight donde indicamos que el método al que va dirigido:
@CrossDomainOptions( method = "preflightedCorsTest2" ) @Path("/preflighted/{param}") @OPTIONS public Response preflightedCorsTestCorsOptions2() { return null; }
Ejemplo de CORS con preflight o autorización previa, donde tenemos varios métodos con el mismo valor de @Path y que además tienen el mismo nombre. En este caso para gestionar bien el preflight, debemos indicar en la anotación @CrossDomainOptions, la propiedad method con el nombre del método correcto, y en la propiedad parameterTypes el tipo de los parámetros del método.
@CrossDomain(methods = { RequestMethod.GET, RequestMethod.OPTIONS }, origins = "https://www.mydomain.com") @Path("/preflighted/{param}") @GET @Produces( MediaType.APPLICATION_JSON ) public Response preflightedCorsTest(@QueryParam("param") String param) { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); } @CrossDomain(methods = { RequestMethod.GET, RequestMethod.OPTIONS }, origins = "https://www.mydomain.com") @Path("/preflighted/{param}") @GET @Produces( MediaType.APPLICATION_XML ) public Response preflightedCorsTest(@QueryParam("param") Integer param) { return Response.status( 200 ).entity( "Cors Simple Ok." ).build(); }
Configuración para el preflight donde indicamos que el método y el tipo de sus parçametros al que va dirigido:
@CrossDomainOptions( method = "preflightedCorsTest", parameterTypes = { Integer.class } ) @Path("/preflighted/{param}") @OPTIONS public Response preflightedCorsTestCorsOptions() { return null; }
La guía que esta a continucación esta OBSOLETA.
Para añadir el soporte CORS a nuestros servicios REST, basta con añadir unas determinadas cabeceras a nuestra respuesta y modificar nuestro método actual para que las añada.
En primer lugar, el tipo de retorno de nuestro método, debe cambiar al tipo Response y deberemos introducir el objeto respuesta dentro de una clase del tipo GenericEntity. En el siguiente ejemplo mostramos cómo quedaría un método así:
import javax.ws.rs.core.GenericEntity; import javax.ws.rs.core.Response; ... ... @GET @Path( "/metodo/{param1}/{param2}" ) @Produces( MediaType.APPLICATION_JSON + ";charset=utf-8" ) public Response corsEjemplo( @PathParam( "param1" ) String param1, @PathParam( "param2" ) String param2) { .... GenericEntity entity = new GenericEntity<ClaseObjetoRespuesta>( objetoRespuesta ) {}; return makeCORS( Response.ok( entity ) ); }
El siguiente paso es crear un método para recuperar las opciones del recurso al que estamos accediendo. Este nuevo método se llamará como el anterior pero los parámetros y las anotaciones serán diferentes. A continuación tenéis un ejemplo
private String corsHeaders; ... @OPTIONS @Produces( MediaType.APPLICATION_JSON + ";charset=utf-8" ) @Path( "/metodo/{param1}/{param2}" ) public Response corsEjemplo( @HeaderParam( "Access-Control-Request-Headers" ) String requestH ) { corsHeaders = requestH; return makeCORS( Response.ok(), requestH ); }
A continuación debemos implementar el método makeCORS que es el que añadirá las cabeceras necesarias. El código es el siguiente:
private Response makeCORS( ResponseBuilder req ) { return makeCORS( req, corsHeaders ); } private Response makeCORS( ResponseBuilder req, String returnMethod ) { ResponseBuilder rb = req.header( "Access-Control-Allow-Origin", "*" ).header( "Access-Control-Allow-Methods", "GET, POST, OPTIONS" ); if ( !"".equals( returnMethod ) ) { rb.header( "Access-Control-Allow-Headers", returnMethod ); } return rb.build(); }