Maison > Article > interface Web > Partage de ressources inter-domaines CORS explication détaillée_compétences javascript
Il permet au navigateur d'effectuer des XMLHttpRequest
requêtes vers des serveurs d'origine croisée, surmontant ainsi la limitation selon laquelle AJAX ne peut être utilisé qu'à partir de la même origine.
Cet article présente en détail le mécanisme interne de CORS.
(Description de la photo : prise au parc Oasis à Al Ain, Émirats arabes unis)
1. Introduction
CORS nécessite la prise en charge du navigateur et du serveur. Actuellement, tous les navigateurs prennent en charge cette fonction et le navigateur IE ne peut pas être inférieur à IE10.
L'ensemble du processus de communication CORS est automatiquement complété par le navigateur et ne nécessite pas la participation de l'utilisateur. Pour les développeurs, il n'y a aucune différence entre la communication CORS et la communication AJAX provenant de la même source, et le code est exactement le même. Une fois que le navigateur découvre que la requête AJAX est d'origine croisée, il ajoutera automatiquement des informations d'en-tête supplémentaires, et parfois une requête supplémentaire sera effectuée, mais l'utilisateur ne la ressentira pas.
Par conséquent, la clé pour parvenir à la communication CORS est le serveur. Tant que le serveur implémente l'interface CORS, la communication entre origines est possible.
2. Deux types de demandes
Les navigateurs divisent les requêtes CORS en deux catégories : les requêtes simples et les requêtes pas si simples.
Tant que les deux conditions suivantes sont remplies en même temps, il s'agit d'une simple demande.
(1) La méthode de requête est l'une des trois méthodes suivantes :
POSTE DE TÊTE
(2) Les informations d'en-tête HTTP ne dépassent pas les champs suivants :
AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type : limité à trois valeurs application/x-www-form-urlencoded
, multipart/form-data
, text/plain
Toute demande qui ne remplit pas simultanément les deux conditions ci-dessus est une demande non simple.
Les navigateurs traitent ces deux requêtes différemment.
3. Demande simple 3.1 Processus de base
Pour les requêtes simples, le navigateur émet directement des requêtes CORS. Plus précisément, il s'agit d'ajouter un champ Origin
aux informations d'en-tête.
Ce qui suit est un exemple. Le navigateur détecte que cette requête AJAX d'origine croisée est une requête simple et ajoute automatiquement un champ Origin
aux informations d'en-tête.
GET /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
Dans les informations d'en-tête ci-dessus, le champ Origin
est utilisé pour indiquer de quelle source provient cette requête (protocole + nom de domaine + port). Le serveur décide d'accepter ou non la demande en fonction de cette valeur.
Si la source spécifiée par Origin
n'est pas dans la plage d'autorisations, le serveur renverra une réponse HTTP normale. Lorsque le navigateur découvre que les informations d'en-tête de cette réponse ne contiennent pas le champ Access-Control-Allow-Origin
(voir ci-dessous pour plus de détails), il sait que quelque chose s'est mal passé et renvoie une erreur, qui est détectée par la fonction de rappel XMLHttpRequest
de onerror
. Notez que cette erreur ne peut pas être identifiée par le code d'état, car le code d'état de la réponse HTTP peut être 200.
Si le nom de domaine spécifié par Origin
se situe dans la plage d'autorisations, la réponse renvoyée par le serveur comportera plusieurs champs d'informations d'en-tête supplémentaires.
Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Credentials: trueAccess-Control-Expose-Headers: FooBarContent-Type: text/html; charset=utf-8
Parmi les informations d'en-tête ci-dessus, il y a trois champs liés aux requêtes CORS, tous commençant par Access-Control-
.
(1) Contrôle d'accès-Autoriser-Origine
Ce champ est obligatoire. Sa valeur est soit la valeur du champ Origin
lors de la requête, soit un *
, indiquant que les requêtes de n'importe quel nom de domaine sont acceptées.
(2) Contrôle d'accès-Autoriser-Credentials
Ce champ est facultatif. Sa valeur est une valeur booléenne indiquant s'il faut autoriser l'envoi de cookies. Par défaut, les cookies ne sont pas inclus dans les requêtes CORS. Défini sur true
, ce qui signifie que le serveur autorise explicitement que le cookie soit inclus dans la requête et envoyé au serveur. Cette valeur ne peut être définie que sur true
. Si le serveur ne souhaite pas que le navigateur envoie des cookies, supprimez simplement ce champ.
(3) Access-Control-Expose-Headers
Ce champ est facultatif. Lors d'une requête CORS, la méthode XMLHttpRequest
de l'objet getResponseHeader()
ne peut obtenir que 6 champs de base : Cache-Control
, Content-Language
, Content-Type
, Expires
, Last-Modified
, Pragma
. Si vous souhaitez obtenir d'autres champs, vous devez les préciser en Access-Control-Expose-Headers
. L'exemple ci-dessus précise que getResponseHeader('FooBar')
peut renvoyer la valeur du champ FooBar
.
3.2 attribut withCredentials
Comme mentionné ci-dessus, les requêtes CORS n'envoient pas d'informations d'authentification Cookie et HTTP par défaut. Si vous souhaitez envoyer des cookies au serveur, vous avez besoin du consentement du serveur et précisez le champ Access-Control-Allow-Credentials
.
<code>Access-Control-Allow-Credentials: true</code>
D'un autre côté, les développeurs doivent activer l'attribut withCredentials
dans les requêtes AJAX.
<code>var xhr = new XMLHttpRequest();xhr.withCredentials = true;</code>
否则,即使服务器同意发送Cookie,浏览器也不会发送。或者,服务器要求设置Cookie,浏览器也不会处理。
但是,如果省略withCredentials
设置,有的浏览器还是会一起发送Cookie。这时,可以显式关闭withCredentials
。
<code>xhr.withCredentials = false;</code>
需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。
四、非简单请求4.1 预检请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。
下面是一段浏览器的JavaScript脚本。
var url = 'http://api.alice.com/cors';var xhr = new XMLHttpRequest();xhr.open('PUT', url, true);xhr.setRequestHeader('X-Custom-Header', 'value');xhr.send();
上面代码中,HTTP请求的方法是PUT
,并且发送一个自定义头信息X-Custom-Header
。
浏览器发现,这是一个非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息。
<code>OPTIONS /cors HTTP/1.1Origin: http://api.bob.comAccess-Control-Request-Method: PUTAccess-Control-Request-Headers: X-Custom-HeaderHost: api.alice.comAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...</code>
"预检"请求用的请求方法是OPTIONS
,表示这个请求是用来询问的。头信息里面,关键字段是Origin
,表示请求来自哪个源。
除了Origin
字段,"预检"请求的头信息包括两个特殊字段。
(1)Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
。
(2)Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
。
4.2 预检请求的回应
服务器收到"预检"请求以后,检查了Origin
、Access-Control-Request-Method
和Access-Control-Request-Headers
字段以后,确认允许跨源请求,就可以做出回应。
HTTP/1.1 200 OKDate: Mon, 01 Dec 2008 01:15:39 GMTServer: Apache/2.0.61 (Unix)Access-Control-Allow-Origin: http://api.bob.comAccess-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderContent-Type: text/html; charset=utf-8Content-Encoding: gzipContent-Length: 0Keep-Alive: timeout=2, max=100Connection: Keep-AliveContent-Type: text/plain
上面的HTTP回应中,关键的是Access-Control-Allow-Origin
字段,表示<a href="http://api.bob.com">http://api.bob.com</a>
可以请求数据。该字段也可以设为星号,表示同意任意跨源请求。
Access-Control-Allow-Origin: *
如果浏览器否定了"预检"请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest
对象的onerror
回调函数捕获。控制台会打印出如下的报错信息。
XMLHttpRequest cannot load http://api.alice.com.Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.
服务器回应的其他CORS相关字段如下。
<code>Access-Control-Allow-Methods: GET, POST, PUTAccess-Control-Allow-Headers: X-Custom-HeaderAccess-Control-Allow-Credentials: trueAccess-Control-Max-Age: 1728000</code>
(1)Access-Control-Allow-Methods
该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
(2)Access-Control-Allow-Headers
如果浏览器请求包括Access-Control-Request-Headers
字段,则Access-Control-Allow-Headers
字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
(3)Access-Control-Allow-Credentials
该字段与简单请求时的含义相同。
(4)Access-Control-Max-Age
Ce champ est facultatif et permet de préciser la durée de validité de cette demande de contrôle en amont, en secondes. Dans le résultat ci-dessus, la période de validité est de 20 jours (1 728 000 secondes), ce qui signifie que la réponse peut être mise en cache pendant 1 728 000 secondes (20 jours). Pendant cette période, il n'est pas nécessaire d'émettre une autre demande de contrôle en amont.
4.3 Demande normale et réponse du navigateur
Une fois que le serveur a réussi la requête "preflight", chaque requête CORS normale du navigateur sera la même qu'une simple requête, et il y aura un Origin
champ d'informations d'en-tête. La réponse du serveur aura également un champ d'informations d'en-tête Access-Control-Allow-Origin
.
Ce qui suit est la requête CORS normale du navigateur après la requête « contrôle en amont ».
PUT /cors HTTP/1.1Origin: http://api.bob.comHost: api.alice.comX-Custom-Header: valueAccept-Language: en-USConnection: keep-aliveUser-Agent: Mozilla/5.0...
Le champ Origin
dans les informations d'en-tête ci-dessus est automatiquement ajouté par le navigateur.
Ce qui suit est une réponse normale du serveur.
Access-Control-Allow-Origin: http://api.bob.comContent-Type: text/html; charset=utf-8
Dans les informations d'en-tête ci-dessus, le champ Access-Control-Allow-Origin
doit être inclus dans chaque réponse.
5. Comparaison avec JSONP
CORS est utilisé dans le même but que JSONP, mais est plus puissant que JSONP.
JSONP ne prend en charge que les GET
requêtes, CORS prend en charge tous les types de requêtes HTTP. L'avantage de JSONP est qu'il prend en charge les anciens navigateurs et peut demander des données à des sites Web qui ne prennent pas en charge CORS.
(Fin)