web-dev-qa-db-fra.com

Vous ne savez pas comment gérer les demandes de contrôle en amont de CORS OPTIONS

Je suis novice dans le travail avec Cross Origin Resource Sharing et j'essaie d'obtenir que ma webapp réponde aux demandes CORS. Ma webapp est une application Spring 3.2 exécutée sur Tomcat 7.0.42.

Dans le web.xml de ma webapp, j'ai activé le filtre Tomcat CORS:

<!-- Enable CORS (cross Origin resource sharing) -->
<!-- http://Tomcat.Apache.org/Tomcat-7.0-doc/config/filter.html#CORS_Filter -->
<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.Apache.catalina.filters.CorsFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>   

Mon client (écrit avec AngularJS 1.2.12) tente d'accéder à un point de terminaison REST avec l'authentification de base activée. Lorsqu'il fait sa demande GET, Chrome est le premier contrôle en amont de la demande, mais reçoit une réponse 403 interdite du serveur:

Request URL:http://dev.mydomain.com/joeV2/users/listUsers
Request Method:OPTIONS
Status Code:403 Forbidden
Request Headers:
   OPTIONS /joeV2/users/listUsers HTTP/1.1
   Host: dev.mydomain.com
   Connection: keep-alive
   Cache-Control: max-age=0
   Access-Control-Request-Method: GET
   Origin: http://localhost:8000
   User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36
   Access-Control-Request-Headers: accept, authorization
   Accept: */*
   Referer: http://localhost:8000/
   Accept-Encoding: gzip,deflate,sdch
   Accept-Language: en-US,en;q=0.8
Response Headers:
   HTTP/1.1 403 Forbidden
   Date: Sat, 15 Feb 2014 02:16:05 GMT
   Content-Type: text/plain; charset=UTF-8
   Content-Length: 0
   Connection: close

Je ne sais pas trop comment procéder. Le filtre Tomcat, par défaut , accepte l'en-tête OPTIONS pour accéder à la ressource.

Le problème, je crois, est que ma ressource (l'URL de demande) http://dev.mydomain.com/joeV2/users/listUsers est configurée pour n'accepter que les méthodes GET:

@RequestMapping( method=RequestMethod.GET, value="listUsers", produces=MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public List<User> list(){
    return userService.findAllUsers();
}

Cela signifie-t-il que je dois également faire en sorte que cette méthode/point final accepte la méthode OPTIONS? Si tel est le cas, cela signifie-t-il que je dois explicitement faire en sorte que chaque point de terminaison REST accepte la méthode OPTIONS? À part le code encombrant, je ne sais pas comment cela fonctionnerait même. D'après ce que je comprends du contrôle en amont OPTIONS est pour le navigateur de valider que le navigateur doit avoir accès à la ressource spécifiée. Ce que je comprends pour signifier que ma méthode de contrôleur ne devrait même pas être appelée pendant le contrôle en amont. Donc, spécifier OPTIONS comme méthode acceptée serait contre-productif.

Tomcat devrait-il répondre directement à la demande OPTIONS sans même accéder à mon code? Si oui, manque-t-il quelque chose dans ma configuration?

24
Eric B.

Je me suis assis et j'ai débogué à travers le org.Apache.catalina.filters.CorsFilter pour comprendre pourquoi la demande était interdite. Espérons que cela puisse aider quelqu'un à l'avenir.

Selon le W3 CORS Spec Section 6.2 Preflight Requests , le contrôle en amont doit rejeter la demande si un en-tête soumis ne correspond pas aux en-têtes autorisés.

configuration par défaut du CorsFiltercors.allowed.headers (comme le vôtre) n'inclut pas l'en-tête Authorization qui est soumis avec la demande.

J'ai mis à jour le cors.allowed.headers paramètre de filtre pour accepter l'en-tête authorization et la demande de contrôle en amont est maintenant réussie.

<filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.Apache.catalina.filters.CorsFilter</filter-class>
    <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization</param-value>
    </init-param>     
</filter>

Bien sûr, je ne sais pas pourquoi l'en-tête authorization n'est pas autorisé par défaut par le filtre CORS.

43
Eric B.

La première chose que j'essaierais est de définir vos en-têtes communs pour vos requêtes http qui angular envoie, en insérant le bloc de configuration suivant sur votre module:

.config(function($httpProvider){
    $httpProvider.defaults.headers.common = {};
    $httpProvider.defaults.headers.post = {};
    $httpProvider.defaults.headers.put = {};
    $httpProvider.defaults.headers.patch = {};
})

Celles-ci sont déjà censées être définies par défaut, mais j'ai constaté que je dois souvent le faire manuellement dans ma configuration en raison de toute substitution d'autres modules ou du processus de démarrage interne angular angulaire).

Votre filtre CORS devrait être suffisant côté serveur pour autoriser ces types de demandes, mais parfois, vous devez spécifier des méthodes de demande en plus de vos origines, ainsi que des types de contenu acceptés. Les documents Tomcat ont ce bloc avancé, qui les corrige.

 <filter>
  <filter-name>CorsFilter</filter-name>
  <filter-class>org.Apache.catalina.filters.CorsFilter</filter-class>
  <init-param>
    <param-name>cors.allowed.origins</param-name>
    <param-value>*</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.methods</param-name>
    <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
  </init-param>
  <init-param>
    <param-name>cors.allowed.headers</param-name>
    <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
  </init-param>
  <init-param>
    <param-name>cors.exposed.headers</param-name>
    <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
  </init-param>
  <init-param>
    <param-name>cors.support.credentials</param-name>
    <param-value>true</param-value>
  </init-param>
  <init-param>
    <param-name>cors.preflight.maxage</param-name>
    <param-value>10</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>CorsFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Si le premier ne fonctionne pas seul, essayez d'améliorer vos filtres, en particulier:

<init-param>
        <param-name>cors.allowed.methods</param-name>
        <param-value>GET,POST,HEAD,OPTIONS,PUT</param-value>
      </init-param>
      <init-param>
        <param-name>cors.allowed.headers</param-name>
        <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
      </init-param>
      <init-param>
        <param-name>cors.exposed.headers</param-name>
        <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
      </init-param>
4
Brian Vanderbusch