java - resource - Cómo manejar CORS usando JAX-RS con Jersey
enable cors java web service (6)
Estoy desarrollando una aplicación cliente Java Script, en el lado del servidor necesito manejar CORS, todos los servicios que había escrito en JAX-RS con JERSEY. Mi código:
@CrossOriginResourceSharing(allowAllOrigins = true)
@GET
@Path("/readOthersCalendar")
@Produces("application/json")
public Response readOthersCalendar(String dataJson) throws Exception {
//my code. Edited by gimbal2 to fix formatting
return Response.status(status).entity(jsonResponse).header("Access-Control-Allow-Origin", "*").build();
}
A partir de ahora, recibo el error No hay encabezado ''Access-Control-Allow-Origin'' presente en el recurso solicitado. Por lo tanto, el origen '' http: // localhost: 8080 '' no tiene acceso permitido ”.
Por favor, ayúdame con esto.
Gracias y Saludos Buda Puneeth
Nota: asegúrese de leer la ACTUALIZACIÓN en la parte inferior
@CrossOriginResourceSharing
es una anotación CXF, por lo que no funcionará con Jersey.
Con Jersey, para manejar CORS, normalmente solo uso un
ContainerResponseFilter
.
ContainerResponseFilter
para Jersey 1 y 2 son un poco diferentes.
Como no ha mencionado qué versión está utilizando, publicaré ambas.
Jersey 2
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
@Provider
public class CORSFilter implements ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request,
ContainerResponseContext response) throws IOException {
response.getHeaders().add("Access-Control-Allow-Origin", "*");
response.getHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
}
}
Si usa el escaneo de paquetes para descubrir proveedores y recursos, la anotación
@Provider
debe ocuparse de la configuración por usted.
De lo contrario, deberá registrarlo explícitamente en
ResourceConfig
o en la subclase
Application
.
Código de muestra para registrar explícitamente el filtro con
ResourceConfig
:
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);
Para Jersey 2.x, si tiene problemas para registrar este filtro, aquí hay algunos recursos que podrían ayudarlo
- Registro de recursos y proveedores en Jersey 2
- ¿Qué es exactamente la clase ResourceConfig en Jersey 2?
Jersey 1
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;
public class CORSFilter implements ContainerResponseFilter {
@Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
response.getHttpHeaders().add("Access-Control-Allow-Origin", "*");
response.getHttpHeaders().add("Access-Control-Allow-Headers",
"origin, content-type, accept, authorization");
response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHttpHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
return response;
}
}
configuración web.xml, puede usar
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.CORSFilter</param-value>
</init-param>
O
ResourceConfig
que puedes hacer
resourceConfig.getContainerResponseFilters().add(new CORSFilter());
O escanee paquetes con la anotación
@Provider
.
EDITAR
Tenga en cuenta que el ejemplo anterior se puede mejorar.
Necesitará saber más sobre cómo funciona CORS.
Por favor mira
here
.
Por un lado, obtendrá los encabezados de todas las respuestas.
Esto puede no ser deseable.
Es posible que solo necesite manejar la verificación previa (u OPCIONES).
Si desea ver un filtro CORS mejor implementado, puede consultar el código fuente del
RESTeasy
CorsFilter
ACTUALIZAR
Entonces decidí agregar una implementación más correcta. La implementación anterior es perezosa y agrega todos los encabezados CORS a todas las solicitudes. El otro error es que, dado que es solo un filtro de respuesta , la solicitud aún se procesa. Esto significa que cuando llega la solicitud de verificación previa, que es una solicitud de OPTIONS, no se implementará ningún método OPTIONS, por lo que obtendremos una respuesta 405, que es incorrecta.
Así es como
debería
funcionar.
Por lo tanto, hay dos tipos de solicitudes CORS: solicitudes simples y
solicitudes de verificación previa
.
Para una solicitud simple, el navegador enviará la solicitud real y agregará el encabezado de solicitud de
Origin
.
El navegador espera que la respuesta tenga el encabezado
Access-Control-Allow-Origin
y dice que el origen del encabezado
Origin
está permitido.
Para que se considere una "solicitud simple", debe cumplir con los siguientes criterios:
-
Sé uno de los siguientes métodos:
- OBTENER
- CABEZA
- ENVIAR
-
Además de los encabezados configurados automáticamente por el navegador, la solicitud solo puede contener los siguientes encabezados configurados
manualmente
:
-
Accept
-
Accept-Language
-
Content-Language
-
Content-Type
-
DPR
-
Save-Data
-
Viewport-Width
-
Width
-
-
Los únicos valores permitidos para el encabezado
Content-Type
son:-
application/x-www-form-urlencoded
-
multipart/form-data
-
text/plain
-
Si la solicitud no cumple con todos estos tres criterios, se realiza una solicitud Preflight.
Esta es una solicitud de OPCIONES que se realiza al servidor,
antes de
que se realice la solicitud real.
Contendrá diferentes encabezados
Access-Control-XX-XX
, y el servidor debe responder a esos encabezados con sus propios encabezados de respuesta CORS.
Aquí están los encabezados coincidentes:
Preflight Request and Response Headers
+-----------------------------------+--------------------------------------+
| REQUEST HEADER | RESPONSE HEADER |
+===================================+======================================+
| Origin | Access-Control-Allow-Origin |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Headers | Access-Control-Allow-Headers |
+-----------------------------------+--------------------------------------+
| Access-Control-Request-Method | Access-Control-Allow-Methods |
+-----------------------------------+--------------------------------------+
| XHR.withCredentials | Access-Control-Allow-Credentials |
+-----------------------------------+--------------------------------------+
-
Con el encabezado de solicitud de
Origin
, el valor será el dominio del servidor de origen, y la respuestaAccess-Control-Allow-Header
debe ser esta misma dirección o*
para especificar que todos los orígenes están permitidos. -
Si el cliente intenta configurar manualmente los encabezados que no están en la lista anterior, entonces el navegador establecerá el
Access-Control-Request-Headers
, con el valor de ser una lista de todos los encabezados que el cliente está tratando de configurar. El servidor debe responder con unAccess-Control-Allow-Headers
respuestaAccess-Control-Allow-Headers
, siendo el valor una lista de encabezados que permite. -
El navegador también establecerá el encabezado de solicitud
Access-Control-Request-Method
, siendo el valor el método HTTP de la solicitud. El servidor debe responder con el encabezado de respuestaAccess-Control-Allow-Methods
, siendo el valor una lista de los métodos que permite. -
Si el cliente usa
XHR.withCredentials
, entonces el servidor debe responder con el encabezado de respuestaAccess-Control-Allow-Credentials
, con un valortrue
. Lee más aquí .
Entonces, con todo lo dicho, aquí hay una mejor implementación.
Aunque esto es
mejor
que la implementación anterior, sigue siendo inferior al
RESTEasy al que
me
vinculé
, ya que esta implementación aún permite todos los orígenes.
Pero este filtro cumple mejor las especificaciones CORS que el filtro anterior, que solo agrega los encabezados de respuesta CORS a todas las solicitudes.
Tenga en cuenta que también es posible que deba modificar el
Access-Control-Allow-Headers
para que coincida con los encabezados que permitirá su aplicación;
es posible que desee agregar o eliminar algunos encabezados de la lista en este ejemplo.
@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
/**
* Method for ContainerRequestFilter.
*/
@Override
public void filter(ContainerRequestContext request) throws IOException {
// If it''s a preflight request, we abort the request with
// a 200 status, and the CORS headers are added in the
// response filter method below.
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
return;
}
}
/**
* A preflight request is an OPTIONS request
* with an Origin header.
*/
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null
&& request.getMethod().equalsIgnoreCase("OPTIONS");
}
/**
* Method for ContainerResponseFilter.
*/
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response)
throws IOException {
// if there is no Origin header, then it is not a
// cross origin request. We don''t do anything.
if (request.getHeaderString("Origin") == null) {
return;
}
// If it is a preflight request, then we add all
// the CORS headers here.
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers",
// Whatever other non-standard/safe headers (see list above)
// you want the client to be able to send to the server,
// put it in this list. And remove the ones you don''t want.
"X-Requested-With, Authorization, " +
"Accept-Version, Content-MD5, CSRF-Token");
}
// Cross origin requests can be either simple requests
// or preflight request. We need to add this header
// to both type of requests. Only preflight requests
// need the previously added headers.
response.getHeaders().add("Access-Control-Allow-Origin", "*");
}
}
Para obtener más información sobre CORS, sugiero leer los documentos de MDN sobre here
Con
JAX-RS
, simplemente puede agregar la anotación
@CrossOrigin(origin = yourURL)
a su controlador de recursos.
En su caso, sería
@CrossOrigin(origin = "http://localhost:8080")
pero también podría usar
@CrossOrigin(origin = "*")
para permitir que cualquier solicitud pase por su servicio web.
Puede consultar
THIS
para obtener más información.
Eliminar la anotación "
@CrossOriginResourceSharing(allowAllOrigins = true)
"
Luego, responda la respuesta como a continuación:
return Response.ok()
.entity(jsonResponse)
.header("Access-Control-Allow-Origin", "*")
.build();
¡Pero el
jsonResponse
debería reemplazarse con un objeto POJO!
La otra respuesta puede ser estrictamente correcta, pero engañosa. La parte que falta es que puedes mezclar filtros de diferentes fuentes. Aunque pensé que Jersey podría no proporcionar el filtro CORS (no es un hecho que verifiqué, pero confío en la otra respuesta al respecto), puede usar el filtro CORS de tomcat .
Lo estoy usando con éxito con Jersey. Tengo mi propia implementación del filtro de autenticación básica, por ejemplo, junto con CORS. Lo mejor de todo es que el filtro CORS está configurado en XML web, no en código.
La respuesta de peeskillet es correcta. Pero recibo este error cuando actualizo la página web (solo funciona en la primera carga):
The ''Access-Control-Allow-Origin'' header contains multiple values ''*, *'', but only one is allowed. Origin ''http://127.0.0.1:8080'' is therefore not allowed access.
Entonces, en lugar de usar el método add para agregar encabezados para la respuesta, uso el método put. Esta es mi clase
public class MCORSFilter implements ContainerResponseFilter {
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
public static final String ACCESS_CONTROL_ALLOW_ORIGIN_VALUE = "*";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
private static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE = "true";
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
public static final String ACCESS_CONTROL_ALLOW_HEADERS_VALUE = "Cache-Control, Pragma, Origin, Authorization, Content-Type, X-Requested-With, Accept";
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
public static final String ACCESS_CONTROL_ALLOW_METHODS_VALUE = "GET, POST, PUT, DELETE, OPTIONS, HEAD";
public static final String[] ALL_HEADERs = {
ACCESS_CONTROL_ALLOW_ORIGIN,
ACCESS_CONTROL_ALLOW_CREDENTIALS,
ACCESS_CONTROL_ALLOW_HEADERS,
ACCESS_CONTROL_ALLOW_METHODS
};
public static final String[] ALL_HEADER_VALUEs = {
ACCESS_CONTROL_ALLOW_ORIGIN_VALUE,
ACCESS_CONTROL_ALLOW_CREDENTIALS_VALUE,
ACCESS_CONTROL_ALLOW_HEADERS_VALUE,
ACCESS_CONTROL_ALLOW_METHODS_VALUE
};
@Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
for (int i = 0; i < ALL_HEADERs.length; i++) {
ArrayList<Object> value = new ArrayList<>();
value.add(ALL_HEADER_VALUEs[i]);
response.getHttpHeaders().put(ALL_HEADERs[i], value); //using put method
}
return response;
}
}
Y agregue esta clase a init-param en web.xml
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>com.yourpackage.MCORSFilter</param-value>
</init-param>
Para resolver esto para mi proyecto, utilicé respuesta y llegué a esto:
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<executions>
<execution>
<id>run-embedded</id>
<goals>
<goal>run</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<port>${maven.tomcat.port}</port>
<useSeparateTomcatClassLoader>true</useSeparateTomcatClassLoader>
<contextFile>${project.basedir}/tomcat/context.xml</contextFile>
<!--enable CORS for development purposes only. The web.xml file specified is a copy of
the auto generated web.xml with the additional CORS filter added -->
<tomcatWebXml>${maven.tomcat.web-xml.file}</tomcatWebXml>
</configuration>
</execution>
</executions>
</plugin>
El filtro CORS es el filtro de ejemplo básico del
sitio tomcat.
Editar
:
La variable
maven.tomcat.web-xml.file
es una propiedad definida por pom para el proyecto y contiene la ruta al archivo web.xml (ubicado dentro de mi proyecto)