web-dev-qa-db-fra.com

Comment gérer CORS avec JAX-RS avec Jersey

Je développe une application client script Java, côté serveur, j'ai besoin de gérer CORS, tous les services que j'avais écrits dans JAX-RS avec JERSEY . Mon code:

@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();
}

Je reçois à présent l'erreur. Aucun en-tête 'Access-Control-Allow-Origin' n'est présent sur la ressource demandée. L'origine ' http: // localhost: 8080 ' n'est donc pas autorisée. ”

S'il vous plaît aidez-moi avec cela.

Merci et salutations Bouddha Puneeth

53
user2773716

Note: Assurez-vous de lire UPDATE en bas

@CrossOriginResourceSharing est une annotation CXF, cela ne fonctionnera donc pas avec Jersey. 

Avec Jersey, pour manipuler CORS, j’utilise normalement juste un ContainerResponseFilter . Les ContainerResponseFilter pour Jersey 1 et 2 sont un peu différentes. Puisque vous n'avez pas mentionné la version que vous utilisez, je posterai les deux.

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 vous utilisez l'analyse de package pour découvrir des fournisseurs et des ressources, l'annotation @Provider doit gérer la configuration pour vous. Sinon, vous devrez explicitement l'enregistrer dans la sous-classe ResourceConfig ou Application.

Exemple de code pour enregistrer explicitement le filtre avec la variable ResourceConfig:

final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new CORSFilter());
final final URI uri = ...;
final HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(uri, resourceConfig);

Pour Jersey 2.x, si vous rencontrez des problèmes pour enregistrer ce filtre, voici quelques ressources pouvant vous aider.

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;
    }
}

configuration web.xml, vous pouvez utiliser 

<init-param>
  <param-name>com.Sun.jersey.spi.container.ContainerResponseFilters</param-name>
  <param-value>com.yourpackage.CORSFilter</param-value>
</init-param>

Ou ResourceConfig vous pouvez faire

resourceConfig.getContainerResponseFilters().add(new CORSFilter());

Ou analyse de package avec l'annotation @Provider.


MODIFIER

Veuillez noter que l'exemple ci-dessus peut être amélioré. Vous aurez besoin d'en savoir plus sur le fonctionnement de la SCRO. S'il vous plaît voir ici . D'une part, vous obtiendrez les en-têtes de toutes les réponses. Cela peut ne pas être souhaitable. Vous devrez peut-être simplement gérer le contrôle en amont (ou les OPTIONS). Si vous voulez voir un filtre CORS mieux implémenté, vous pouvez consulter le code source de RESTeasy CorsFilter


METTRE À JOUR

J'ai donc décidé d'ajouter une implémentation plus correcte. L'implémentation ci-dessus est paresseuse et ajoute tous les en-têtes CORS à toutes les demandes. L’autre erreur est qu’il n’ya qu’un filtre réponse, la demande est toujours en cours de traitement. Cela signifie que lorsque la demande de contrôle en amont arrive (demande OPTIONS), aucune méthode OPTIONS n'est implémentée. Nous allons donc obtenir une réponse 405, ce qui est incorrect.

Voici comment cela fonctionne devrait Il existe donc deux types de demandes CORS: les demandes simples et les demandes de contrôle en amont . Pour une requête simple, le navigateur envoie la requête réelle et ajoute l'en-tête de la requête Origin. Le navigateur s'attend à ce que la réponse ait l'en-tête Access-Control-Allow-Origin, indiquant que l'origine à partir de l'en-tête Origin est autorisée. Pour qu’elle soit considérée comme une "simple demande", elle doit répondre aux critères suivants:

  • Choisissez l'une des méthodes suivantes:
    • GET
    • HEAD
    • POST
  • Hormis les en-têtes définis automatiquement par le navigateur, la requête ne peut contenir que les en-têtes manuellement définis:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type
    • DPR
    • Save-Data
    • Viewport-Width
    • Width
  • Les seules valeurs autorisées pour l'en-tête Content-Type sont:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Si la demande ne répond pas à ces trois critères, une demande de contrôle en amont est effectuée. Il s'agit d'une requête OPTIONS adressée au serveur, préalable à la requête en cours. Il contiendra différents en-têtes Access-Control-XX-XX et le serveur devrait répondre à ces en-têtes avec ses propres en-têtes de réponse CORS. Voici les en-têtes correspondants:

                 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    |
+-----------------------------------+--------------------------------------+
  • Avec l'en-tête de requête Origin, la valeur correspond au domaine du serveur d'origine et la réponse Access-Control-Allow-Header doit correspondre à la même adresse ou à * pour spécifier que toutes les origines sont autorisées.

  • Si le client tente de définir manuellement des en-têtes ne figurant pas dans la liste ci-dessus, le navigateur définira l'en-tête Access-Control-Request-Headers, la valeur correspondant à la liste de tous les en-têtes que le client tente de définir. Le serveur doit répondre avec un en-tête de réponse Access-Control-Allow-Headers, la valeur étant une liste d'en-têtes qu'il autorise.

  • Le navigateur définira également l'en-tête de demande Access-Control-Request-Method, la valeur étant la méthode HTTP de la demande. Le serveur doit répondre avec l'en-tête de réponse Access-Control-Allow-Methods, la valeur étant une liste des méthodes qu'il autorise.

  • Si le client utilise le XHR.withCredentials, le serveur doit répondre avec l'en-tête de réponse Access-Control-Allow-Credentials, avec la valeur true. Lire la suite ici .

Donc, avec tout ce qui est dit, voici une meilleure mise en œuvre. Même si cela est meilleur que l'implémentation ci-dessus, il est toujours inférieur à RESTEasy one I lié à, car cette implémentation permet toujours toutes les origines. Mais ce filtre adhère mieux à la spécification CORS que le filtre ci-dessus, qui ajoute simplement les en-têtes de réponse CORS à toutes les demandes. Notez que vous devrez peut-être également modifier le Access-Control-Allow-Headers pour qu'il corresponde aux en-têtes autorisés par votre application. vous souhaiterez peut-être ajouter ou supprimer des en-têtes de la liste dans cet exemple.

@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", "*");
    }
}

Pour en savoir plus sur CORS, je suggère de lire les documents MDN sur Partage de ressources d'origine croisée (CORS)

132
Paul Samsotha

L'autre réponse pourrait être strictement correcte, mais trompeuse. La partie manquante est que vous pouvez mélanger des filtres provenant de sources différentes. Même si Jersey ne fournissait peut-être pas de filtre CORS (ce n’est pas un fait que j’ai vérifié mais j’ai confiance en l’autre réponse), vous pouvez utiliser le filtre CORS de Tomcat .

Je l'utilise avec succès avec Jersey. J'ai ma propre implémentation du filtre d'authentification de base, par exemple, avec CORS. Mieux encore, le filtre CORS est configuré en Web XML, pas en code.

4
Michael

peeskillet a répondu correctement. Mais je reçois cette erreur lors de l'actualisation de la page Web (elle ne fonctionne que lors du premier chargement):

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.

Ainsi, au lieu d’utiliser la méthode add pour ajouter des en-têtes de réponse, j’utilise la méthode put. C'est ma classe

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;
    }
}

Et ajoutez cette classe à init-param dans web.xml

<init-param>
            <param-name>com.Sun.jersey.spi.container.ContainerResponseFilters</param-name>
            <param-value>com.yourpackage.MCORSFilter</param-value>
        </init-param>
1
die_for_rock_vn

Pour résoudre ceci pour mon projet, j'ai utilisé Micheal's answer et je suis arrivé à ceci:

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

Le filtre CORS étant l'exemple de base du filtre de le site Tomcat.

Modifier:
La variable maven.Tomcat.web-xml.file est une propriété définie par pom pour le projet et contient le chemin d'accès au fichier web.xml (situé dans mon projet).

1
Dark Star1

Supprimer l'annotation "@CrossOriginResourceSharing(allowAllOrigins = true)"

Puis retour comme ci-dessous:

return Response.ok()
               .entity(jsonResponse)
               .header("Access-Control-Allow-Origin", "*")
               .build();

Mais la jsonResponse devrait être remplacée par un objet POJO!

1
Aupr