cross-domain - jax - java no access control allow origin header is present on the requested resource
¿Cómo habilitar las solicitudes de dominio cruzado en los servicios web JAX-RS? (7)
Desarrollé un conjunto de servicios web relajantes. No pude llamar a ninguno de estos métodos desde clientes remotos debido al error No ''Access-Control-Allow-Origin'' header is present on the requested resource.
Los servicios funcionan perfectamente en localhost. ¿Hay algún cambio o configuración para hacer en el servidor para resolver el problema? es decir, para permitir solicitudes de dominios cruzados.
Estoy usando WildFly 8, JavaEE 7
Encontré una manera aún más fácil (RestEasy-specific) de habilitar CORS en Wildfly sin usar un filtro y donde puedes controlar la configuración de tu encabezado de respuesta de API en el nivel de recursos.
Por ejemplo:
@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();
}
He tenido la suerte de configurar el intercambio de recursos de origen cruzado (CORS) para mi API (en Wildfly) utilizando esta lib:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>2.1</version>
</dependency>
Es muy fácil de configurar. Simplemente agregue la dependencia anterior a su pom y luego agregue la siguiente configuración a la sección de aplicaciones web de su archivo web.xml.
<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>
También puede configurarlo con un archivo de propiedades si lo prefiere. ¡Esta lib funciona como un encanto y te da mucha flexibilidad de configuración!
Me enfrentaba a un problema similar y había tratado de usar la solution @Alex Petty, pero aparte de tener que configurar los encabezados CORS en cada punto final JAX-RS de mi clase, como tal:
@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();
}
Tuve que definir aún más un punto final OPTIONS
catch-all que devolvería los encabezados CORS para cualquier otra solicitud OPTIONS
en la clase, y así atrapar todos los puntos finales del género:
@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();
}
Solo después de hacer esto, ¿podría usar correctamente mis puntos finales JAX-RS API de los clientes de Jquery Ajax en otros dominios o hosts?
Me preguntaba lo mismo, así que después de investigar un poco, descubrí que la forma más fácil era simplemente usar un JAX-RS ContainerResponseFilter
para agregar los encabezados CORS relevantes. De esta forma, no es necesario reemplazar toda la pila de servicios web con CXF (Wildfly usa CXF de alguna forma, pero no parece que lo use para JAX-RS, tal vez solo para JAX-WS).
Independientemente de si usa este filtro, agregará los encabezados a cada servicio web REST.
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");
}
}
Luego, cuando probé con curl, la respuesta tenía los encabezados CORS:
$ 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
@Provider
entendido, es la anotación @Provider
que le dice al JAX-RS que utilice el filtro en tiempo de ejecución, sin que la anotación pase.
Tengo la idea de usar el ContainerResponseFilter
de un ejemplo de Jersey .
Ninguna de las otras respuestas funcionó para mí, pero esto hizo:
import javax.ws.rs.core.Response;
Luego, cambie el tipo de devolución del método de servicio a Response
y cambie la declaración de return
a:
return Response.ok(resp).header("Access-Control-Allow-Origin", "*").build();
Donde resp
es el objeto de respuesta original.
Solo para agregar algo a otras respuestas. Permitir * es un poco peligroso. Lo que se puede hacer es configurar una base de datos del origen permitido (puede ser un archivo)
Luego, cuando llegue la solicitud, puede hacer:
// 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");
}
De esa forma no permitirás que todos
También puede implementar javax.ws.rs.core.Feature
como se javax.ws.rs.core.Feature
continuación para implementar 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;
}
}