CORS pour les requêtes XHR dans IE10
La quatrième plateforme IE10 facilite la création de scénarios intersites fonctionnant de façon homogène avec tous les navigateurs, en prenant en charge la spécification CORS (Cross-Origin Resource Sharing) pour les requêtes XHR (XMLHttpRequest). Avec CORS pour les requêtes XHR, le partage de données entre plusieurs sites devient simple et flexible. À la base, CORS permet de créer des sources de données accessibles depuis n'importe quel site. Quelques manipulations suffisent pour restreindre les sites autorisés, autoriser la modification des données ou même autoriser l'authentification. Par ailleurs, CORS renforce la sécurité des sites existants en obligeant les serveurs à participer.
Requête XHR simple inter-origine
Comparons une requête XHR inter-origine et une requête mono-origine. Du point de vue du script, la seule différence concerne l'URL transmise à la méthode open. Supposons que nous travaillons sur un script chargé de récupérer une liste d'albums photo.
Script d'une requête XHR traditionnelle
// Script running on http://photos.contoso.com
var xhr = new XMLHttpRequest();
xhr.onerror = _handleError;
xhr.onload = _handleLoad;
xhr.open("GET", "/albums", true);
xhr.send();
Nous souhaitons maintenant accéder à la liste des albums depuis une autre origine. Cette origine peut être un autre domaine ou un hôte différent au sein du même domaine de base. Dans tous les cas, il suffit de pointer vers l'URL complète de l'autre site pour que le navigateur envoie automatiquement une requête CORS.
Requête XHR compatible CORS
// Script running on http://www.contoso.com
var xhr = new XMLHttpRequest();
xhr.onerror = _handleError;
xhr.onload = _handleLoad;
xhr.open("GET", "http://photos.contoso.com/albums", true);
xhr.send();
Les sites peuvent fournir une solution de secours destinée aux navigateurs plus anciens, en intégrant le code dans la détection de caractéristiques. La recherche de la chaîne « withCredentials
» constitue la meilleure approche, car elle est directement liée à la prise en charge de la spécification CORS pour les requêtes XHR.
Requête XHR compatible CORS avec détection des caractéristiques
// Script running on http://www.contoso.com
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
xhr.onerror = _handleError;
xhr.onload = _handleLoad;
xhr.open("GET", "http://photos.contoso.com/albums", true);
xhr.send();
} else {
// Fallback behavior for browsers without CORS for XHR
}
À ce stade, notre code client envoie directement une requête CORS vers « http://photos.contoso.com », mais cette requête ne renvoie rien. Cet échec est dû au fait que le serveur ne participe pas encore. Un simple coup d'œil aux outils de développement permet de comprendre le problème.
Nous pouvons remarquer que le serveur doit envoyer un en-tête « Access-Control-Allow-Origin
» dans sa réponse. Dans notre scénario, nous n'ouvrons pas l'accès à nos albums à partir de tous les sites : nous souhaitons uniquement autoriser l'accès depuis « http://www.contoso.com
». Pour cela, nous devons autoriser le serveur à identifier l'origine de la requête. En examinant notre requête sortante, nous pouvons constater qu'elle contient un nouvel en-tête contenant justement cette information, « Origin
».
En-têtes des requêtes CORS simples
GET http://photos.contoso.com/albums HTTP/1.1
Origin: http://www.contoso.com
...
Grâce à ces informations, le serveur peut décider de limiter l'accès à n'importe quel groupe de sites. Si le serveur ajoute toujours un en-tête « Access-Control-Allow-Origin
» dont la valeur est « * », tous les sites auront accès à la cible. Dans notre scénario, le serveur doit vérifier l'origine, puis définir la valeur de l'en-tête « Access-Control-Allow-Origin
» de sorte que seules les requêtes provenant du domaine « http://www.contoso.com
» soient autorisées.
En-têtes des réponses CORS simples
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.contoso.com
...
Une fois les mises à jour ci-dessus réalisées, notre client « http://www.contoso.com
» peut accéder aux listes d'albums à partir du serveur situé à l'adresse « http://photos.contoso.com
».
Requête XHR inter-origine avec contrôle préalable
Les requêtes CORS « simples » examinées jusqu'ici sont idéales pour des scénarios élémentaires en lecture seule (téléchargement d'un album photo, par exemple). L'étape suivante, qui consiste à modifier les données entre des sites, nécessite un peu plus de travail côté serveur. Ajoutons par exemple du code au client pour créer un nouvel album.
var xhr = new XMLHttpRequest();
xhr.onerror = _handleError;
xhr.onload = _handleLoad;
xhr.open("PUT", "http://photos.contoso.com/albums", true);
xhr.send(JSON.stringify({ name: "New Album" }));
Tel quel, ce script ne peut pas fonctionner. En examinant le trafic réseau, nous pouvons constater qu'une requête est envoyée, mais qu'il ne s'agit pas de la requête attendue.
En fait, le navigateur a envoyé une requête de contrôle préalable. Ces requêtes sont envoyées avant les requêtes susceptibles de nécessiter une modification des données côté serveur. Elles sont identifiées par la présence de propriétés non simples, telles que définies dans la spécification CORS. Il peut s'agir de certaines méthodes HTTP telles que « PUT
» ou d'en-têtes HTTP personnalisés. Les navigateurs envoient des requêtes de contrôle préalable pour demander au serveur l'autorisation d'envoyer la requête proprement dite. Dans notre exemple, le navigateur vérifie qu'une requête « PUT
» est autorisée.
Requête de contrôle préalable
OPTIONS http://photos.contoso.com/albums HTTP/1.1
Origin: http://www.contoso.com
Access-Control-Request-Method: PUT
...
Pour que le navigateur envoie la requête proprement dite, des modifications sont nécessaires côté serveur. Là encore, nous pouvons examiner les outils de développement pour en savoir plus.
Nous devons commencer par faire en sorte que le serveur puisse distinguer la requête de contrôle préalable « OPTIONS
» des autres requêtes provenant de la même URL. Une fois que le serveur a vérifié la requête de contrôle préalable (en s'assurant que « Access-Control-Request-Method
» demande la méthode « PUT
» à partir d'une origine autorisée), il envoie l'autorisation adéquate par le biais de l'en-tête « Access-Control-Allow-Methods
».
Réponse de contrôle préalable
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.contoso.com
Access-Control-Allow-Methods: PUT
...
Une fois que le contrôle préalable est terminé et approuvé, la requête proprement dite est envoyée.
Requête réelle
PUT http://photos.contoso.com/albums HTTP/1.1
Origin: http://www.contoso.com
...
À ce stade, l'ajout de l'album est terminé du point de vue technique, mais notre code client ne le sait pas, sauf si le serveur répond correctement. Plus précisément, le serveur doit inclure « Access-Control-Allow-Origin
» dans la réponse.
Réponse réelle
HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://www.contoso.com
...
Ainsi, le client peut ajouter un nouvel album à partir de différentes origines et savoir si l'opération s'est déroulée correctement.
Étapes suivantes
Le fait d'associer la spécification CORS à d'autres fonctionnalités de la nouvelle plateforme permet d'envisager des scénarios intéressants. Par exemple, la démonstration de transfert de fichiers inter-site réalise un suivi des transferts de fichiers inter-origine grâce à CORS, aux requêtes XHR, à l'API FileAPI et aux événements de progression.
—Tony Ross, chef de projet, Internet Explorer