Pourquoi la méthode .ajax () de jquery n'envoie-t-elle pas mon cookie de session?

338

Après m'être connecté via $.ajax()un site, j'essaie d'envoyer une deuxième $.ajax()demande à ce site - mais lorsque je vérifie les en-têtes envoyés à l'aide de FireBug, aucun cookie de session n'est inclus dans la demande.

Qu'est-ce que je fais mal?

user345625
la source
2
Le cookie ajax peut apparaître après le cookie Web et FireBug peut attraper le cookie de première page.
Chris
1
Je n'ai pas compris ce que vous voulez dire, mais je peux dire que si je colle l'URL de la demande dans la barre d'adresse du navigateur et que je vérifie à nouveau Firebug, je peux voir le cookie dans les en-têtes envoyé au serveur. Des solutions?
user345625
Donc, je pense que ajax traitera également de la même manière que le navigateur
user345625
Quel est le code que vous utilisez?
Dean Harding
le navigateur créera toujours des cookies définis par le serveur lors d'une requête ajax, jquery ou autre. Avez-vous vérifié la réponse à la demande ajax et vérifié que les cookies sont revenus du serveur pour être définis? Il pourrait y avoir un problème avec le code du serveur tel qu'il ne place même pas le cookie, etc.
David

Réponses:

218

Les appels AJAX n'envoient des cookies que si l'URL que vous appelez se trouve dans le même domaine que votre script d'appel.

Cela peut être un problème interdomaine.

Vous avez peut-être essayé d'appeler une URL www.domain-a.comlorsque votre script d'appel était activé www.domain-b.com(en d'autres termes: vous avez effectué un appel interdomaine, auquel cas le navigateur n'enverra aucun cookie pour protéger votre confidentialité).

Dans ce cas, vos options sont les suivantes:

  • Écrivez un petit proxy qui réside sur le domaine-b et transmet vos demandes au domaine-a. Votre navigateur vous permettra d'appeler le proxy car il se trouve sur le même serveur que le script appelant.
    Ce proxy peut ensuite être configuré par vous pour accepter un nom de cookie et un paramètre de valeur qu'il peut envoyer à domain-a. Mais pour que cela fonctionne, vous devez connaître le nom du cookie et évaluer votre serveur sur le domaine-a veut l'authentification.
  • Si vous récupérez des objets JSON, essayez plutôt d'utiliser une requête JSONP . jQuery les prend en charge. Mais vous devez modifier votre service sur le domaine-a afin qu'il renvoie des réponses JSONP valides.

Heureux si cela a aidé même un peu.

grippe
la source
19
Il convient également de noter que les cookies peuvent être définis sur un chemin spécifique, donc si votre cookie a été défini avec path=/somethinget que vous demandez la page, /anotherle cookie ne sera pas envoyé. Lorsque vous demandez la page, /somethingle cookie sera envoyé comme prévu. Vérifiez donc également le code qui définit le cookie.
styfle
2
une demande jsonp envoie-t-elle des coockies?
albanx
1
@albanx Oui, si les exigences que j'ai mentionnées sont définies. C'est juste une demande normale comme les autres et en tant que telle envoie des cookies.
grippe
1
@albanx cette autre question connexe comprend un exemple de la façon de faire cette demande JSONP avec des cookies personnalisés
AntonioHerraizS
4
Selon JSONP sur Wikipédia> cette approche a été abandonnée au profit de
CORS
388

J'opère dans un scénario interdomaine. Lors de la connexion, le serveur distant renvoie l'en-tête Set-Cookie avec Access-Control-Allow-Credentialsla valeur true.

Le prochain appel ajax au serveur distant doit utiliser ce cookie.

CORS Access-Control-Allow-Credentialsest là pour permettre la journalisation inter-domaines. Consultez https://developer.mozilla.org/En/HTTP_access_control pour des exemples.

Pour moi, cela ressemble à un bug dans JQuery (ou au moins une fonctionnalité à venir dans la prochaine version).

METTRE À JOUR:

  1. Les cookies ne sont pas définis automatiquement à partir de la réponse AJAX (citation: http://aleembawany.com/2006/11/14/anatomy-of-a-well-designed-ajax-login-experience/ )

    Pourquoi?

  2. Vous ne pouvez pas obtenir la valeur du cookie à partir de la réponse pour le définir manuellement ( http://www.w3.org/TR/XMLHttpRequest/#dom-xmlhttprequest-getresponseheader )

    Je suis confus..

    Il devrait exister un moyen de demander jquery.ajax()de définir le XMLHttpRequest.withCredentials = "true"paramètre.

RÉPONSE: Vous devez utiliser xhrFieldsparam de http://api.jquery.com/jQuery.ajax/

L'exemple dans la documentation est:

$.ajax({
   url: a_cross_domain_url,
   xhrFields: {
      withCredentials: true
   }
});

Il est également important que le serveur réponde correctement à cette demande. Copiant ici les excellents commentaires de @ Frédéric et @Pebbl:

Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: *

Donc, lorsque la demande est:

Origin: http://foo.example
Cookie: pageAccess=2

Le serveur doit répondre avec:

Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true

[payload]

Sinon, la charge utile ne sera pas retournée au script. Voir: https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials

Kangur
la source
8
Génial ! J'ajoute pour utiliser cet + définir l'en-tête Access-Control-Allow-Credentials sur true côté serveur
Frédéric
et où dois-je définir ces pouvoirs?, à l'autorisation d'en-tête?, auprès de l'organisme demandeur?
Francisco Corrales Morales
3
Merci pour la réponse :) juste un ajout rapide, il vaut peut-être la peine de mentionner Important note: when responding to a credentialed request, server must specify a domain, and cannot use wild carding. The above example would fail if the header was wildcarded as: Access-Control-Allow-Origin: * developer.mozilla.org/en-US/docs/Web/HTTP/…
Pebbl
Malheureusement, rien de tout cela n'a fonctionné pour moi. Si j'exécute la même demande d'AngularJS, cela fonctionne, mais depuis jQuery, même avec ces suggestions, le cookie de session n'est pas transmis. (jQuery v2.1.1)
géoidesic
(OO) Vous m'avez sauvé de plusieurs heures douloureuses. Quelle réponse parfaite! Merci! J'avais besoin de les ajouter dans la racine publique .htaccess de mon site Web: <IfModule mod_headers.c> Ensemble d'en-tête Access-Control-Allow-Origin " localhost " Ensemble d'en-tête Access-Control-Allow-Credentials "true" </IfModule>
Vinay Vissh
48

En utilisant

xhrFields: { withCredentials:true }

dans le cadre de mon appel jQuery ajax n'était qu'une partie de la solution. J'ai également dû faire renvoyer les en-têtes dans la réponse OPTIONS de ma ressource:

Access-Control-Allow-Origin : http://www.wombling.com
Access-Control-Allow-Credentials : true

Il était important que seul un permis « origine » était dans l' en- tête de réponse des options d' achat et non « * ». J'ai atteint cet objectif en lisant l'origine de la demande et en la réintroduisant dans la réponse - contournant probablement la raison d'origine de la restriction, mais dans mon cas d'utilisation, la sécurité n'est pas primordiale.

J'ai pensé qu'il valait la peine de mentionner explicitement l'exigence d'une seule origine, car la norme W3C permet une liste séparée par des espaces, mais Chrome ne le fait pas! http://www.w3.org/TR/cors/#access-control-allow-origin-response-header NB le bit "en pratique".

wombling - Chris Paine
la source
41

Mettez ceci dans votre fonction init:

$.ajaxSetup({
  xhrFields: {
    withCredentials: true
  }
});

Ça va marcher.

Alex Athlan
la source
1
Tu m'as sauvé la journée! Au niveau de la méthode withCredentials n'a pas fonctionné pour moi. Mais globalement comme ça ça marche enfin! Merci.
Paulius Matulionis
soyez prudent avec cela, car il enverra des cookies à toutes les demandes, de l'éther pour d'autres domaines (ce qui n'est pas prévu et échouera la demande en exigeant Access-Control-Allow-Credentials)
gdbdable
12

Il y a déjà beaucoup de bonnes réponses à cette question, mais j'ai pensé qu'il pourrait être utile de clarifier le cas où vous vous attendriez à ce que le cookie de session soit envoyé parce que le domaine de cookie correspond, mais il n'est pas envoyé parce que la demande AJAX est être fait à un sous-domaine différent. Dans ce cas, j'ai un cookie qui est affecté au domaine * .mydomain.com , et je souhaite qu'il soit inclus dans une demande AJAX à different.mydomain.com ". Par défaut, le cookie n'est pas envoyé. Vous n'avez pas besoin de désactiver HTTPONLY sur le cookie de session pour résoudre ce problème. Vous devez seulement faire ce que le wombling a suggéré ( https://stackoverflow.com/a/23660618/545223 ) et faire ce qui suit.

1) Ajoutez ce qui suit à votre demande ajax.

xhrFields: { withCredentials:true }

2) Ajoutez ce qui suit à vos en-têtes de réponse pour les ressources dans les différents sous-domaines.

Access-Control-Allow-Origin : http://original.mydomain.com
Access-Control-Allow-Credentials : true
munchbit
la source
7

Après avoir essayé les autres solutions et ne pas toujours les faire fonctionner, j'ai découvert quel était le problème dans mon cas. J'ai changé contentType de "application / json" en "text / plain".

$.ajax(fullUrl, {
    type: "GET",
    contentType: "text/plain",
    xhrFields: {
         withCredentials: true
    },
    crossDomain: true
});
Janno Teelem
la source
4

J'avais ce même problème et en vérifiant mon script, je n'obtenais tout simplement pas le cookie sessionid.

J'ai compris en regardant la valeur du cookie sessionid dans le navigateur que mon framework (Django) passait le cookie sessionid avec HttpOnly par défaut. Cela signifiait que les scripts n'avaient pas accès à la valeur sessionid et ne la transmettaient donc pas avec les requêtes. C'est ridicule que HttpOnly soit la valeur par défaut quand tant de choses utilisent Ajax qui nécessiterait une restriction d'accès.

Pour résoudre ce problème, j'ai modifié un paramètre (SESSION_COOKIE_HTTPONLY = False) mais dans d'autres cas, il peut s'agir d'un indicateur "HttpOnly" sur le chemin du cookie

wiwa
la source
2
Ne faites pas cela. Il permet au script côté client d'accéder au cookie de session qui est le vecteur d'attaque XSS le plus courant. owasp.org/index.php/HttpOnly
Jason Elkin
1

Si vous développez sur localhostou un port sur localhost comme localhost:8080, en plus des étapes décrites dans les réponses ci-dessus, vous devez également vous assurer que vous ne transmettez pas une valeur de domaine dans l'en-tête Set-Cookie.
Vous ne pouvez pas définir le domaine localhostdans l'en-tête Set-Cookie - c'est incorrect - omettez simplement le domaine.

Voir Cookies sur localhost avec domaine explicite et Pourquoi asp.net ne crée-t-il pas de cookies dans localhost?

jitin
la source
0

Juste mes 2 cents sur la définition du problème de cookie PHPSESSID lorsque vous êtes sur un hôte local et dans un environnement de développement. Je fais l'appel AJAX à mon point de terminaison REST API sur le locahost. Disons que son adresse est mysite.localhost/api/member/login/(hôte virtuel sur mon environnement de développement).

  • Quand je fais cette demande sur Postman , les choses vont bien et PHPSESSID est réglé avec la réponse.

  • Lorsque je demande ce point de terminaison via AJAX à partir de la page proxy de Browsersync (par exemple à partir 122.133.1.110:3000/test/api/login.phpde la ligne d'adresse de mon navigateur, voir le domaine est différent vs mysite.localhost) PHPSESSID n'apparaît pas parmi les cookies.

  • Lorsque je fais cette demande directement à partir de la page sur le même domaine (c'est-à-dire mysite.localhost/test/api/login.php) PHPSESSID est très bien défini.

Il s'agit donc d'un problème de cookies de demande d'origine croisée comme mentionné dans la réponse @flu ci-dessus

Valentine Shi
la source
0

Ajouter mon scénario et ma solution au cas où cela aiderait quelqu'un d'autre. J'ai rencontré un cas similaire lors de l'utilisation des API RESTful. Mon serveur Web hébergeant des fichiers HTML / Script / CSS et le serveur d'applications exposant des API étaient hébergés sur le même domaine. Mais le chemin était différent.

serveur web - mydomain / webpages /abc.html

utilisé abc.js qui a défini un cookie nommé mycookie

serveur d'application - mydomain / webapis / servicename .

auquel des appels api ont été effectués

J'attendais le cookie dans mydomain / webapis / servicename et j'ai essayé de le lire, mais il n'était pas envoyé. Après avoir lu le commentaire de la réponse, j'ai vérifié dans l'outil de développement du navigateur que le chemin d'accès de mycookie était défini sur "/ pages Web " et n'était donc pas disponible dans l'appel de service à

mydomain / webapis / servicename

Donc, lors de la configuration des cookies de jquery, voici ce que j'ai fait -

$.cookie("mycookie","mayvalue",{**path:'/'**});
Codeek
la source
-5

Peut-être pas répondre à 100% à la question, mais je suis tombé sur ce fil dans l'espoir de résoudre un problème de session lors de l'ajax-publication d'un fichier depuis le gestionnaire d'actifs de l'éditeur innovastudio. Finalement, la solution était simple: ils ont un uploader flash. Désactiver cela (paramètre

var flashUpload = false;   

dans asset.php) et les voyants ont recommencé à clignoter.

Comme ces problèmes peuvent être très difficiles à déboguer, j'ai trouvé que mettre quelque chose comme ce qui suit dans le gestionnaire de téléchargement vous mettrait (enfin, moi dans ce cas) sur la bonne voie:

$sn=session_name();
error_log("session_name: $sn ");

if(isset($_GET[$sn])) error_log("session as GET param");
if(isset($_POST[$sn])) error_log("session as POST param");
if(isset($_COOKIE[$sn])) error_log("session as Cookie");
if(isset($PHPSESSID)) error_log("session as Global");

Une plongée dans le journal et j'ai rapidement repéré la session manquante, où aucun cookie n'a été envoyé.

Ellert van Koperen
la source
Je ne pense pas que l'exemple ci-dessus fonctionnerait, car lorsqu'il n'y a pas de cookie de session, quelle serait la valeur de $ sn? (aléatoire, ou peut-être null), les utilisateurs pourraient également définir session_name à partir d'une valeur GET, par exemple de session_name(isset($_GET['sess']) ? $_GET['sess'] : null);session_start();cette façon, ils obtiendraient une chose fonctionnelle
Steel Brain
C'est exactement comme ça que j'ai trouvé le problème: pas de session lors de la publication à partir de ce truc flash uploader. Comme utiliser un identifiant de session variable GET est une mauvaise idée, et le cookie ne fonctionnait pas, je l'ai jeté. Peu importe, le flash appartient de toute façon au passé.
Ellert van Koperen