在XMLHttpRequest中设置授权标头会更改HTTP动词

11

今天我发现XMLHttpRequest的一种奇怪行为。当我调用GET服务时,发现如果我不设置Authorization头,那么来自firefox的请求是相同的。但是如果我添加"Authorization"头,则firefox首先发送一个带有"OPTIONS"的请求,然后再发送一个"GET"请求。

我知道动词"OPTIONS"必须在服务器端处理,但我只是想知道为什么XMLHttpRequest会表现出这样的行为。尽管它是跨域请求,但为什么浏览器要先发送"OPTIONS"请求?为什么添加"Authorization"头会改变行为。

以下是我的Javascript代码和Fidler检查报告。

    var  xmlhttp = new XMLHttpRequest();
    var url = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    xmlhttp.open('GET',url,true);
    xmlhttp.setRequestHeader("Authorization", "xxxxxxxxxxxxxxxxxxx");
    xmlhttp.send(null);
    xmlhttp.onreadystatechange = function() {
            alert("OnReadystatechange + " + xmlhttp.readyState + " " + xmlhttp.status);
           if (xmlhttp.readyState == 4) {
              if ( xmlhttp.status == 200) {

                   }
                   else {

                   }
             }
             else
                   alert("Error ->" + xmlhttp.responseText);
          }

并且小提琴手通过授权头做出响应

enter image description here

enter image description here

但是如果我不添加授权头,浏览器会直接发送GET请求而不是OPTIONS请求。

enter image description here

1个回答

12
HTTP的OPTIONS请求用于在实际发送GET请求之前进行"预检"以跨域。
与简单请求不同,“预检”请求首先通过OPTIONS方法向另一个域上的资源发送HTTP请求,以确定实际请求是否安全发送。由于跨站点请求可能对用户数据产生影响,因此会像这样进行预检。特别是,如果请求满足以下条件,则会进行预检: - 它使用除GET、HEAD或POST之外的方法。此外,如果使用POST发送请求数据,并且Content-Type不是application/x-www-form-urlencoded、multipart/form-data或text/plain,例如,如果POST请求使用application/xml或text/xml向服务器发送XML有效负载,则将进行预检。 - 它设置任何非简单标头。如果标题字段名称与Accept、Accept-Language或Content-Language的ASCII大小写不敏感匹配,或者如果它与Content-Type的ASCII大小写不敏感匹配,并且标题字段值媒体类型(不包括参数)与application/x-www-form-urlencoded、multipart/form-data或text/plain的ASCII大小写不敏感匹配,则称该标题为简单标题。
因此,在您的情况下,设置授权标头会导致请求被预检,因此出现OPTIONS请求。

更多信息在此处

关于带有预检的跨源请求的规范


1
是的,我理解预检请求。但只是想知道为什么添加授权标头会使预检请求。这里没有满足链接中提到的两个要求之一。 - gmtek
授权标头被视为“自定义标头”。请参见第二个要求。 - levi
1
看起来是这样。否则就没有任何意义了。我认为这个头部是一个标准头部,因为它在这里被提到(http://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requests)。无论如何,非常感谢您的信息。我是Javascript的新手。您提到的链接确实帮助我很多,让我理解了预检请求。 - gmtek
我也不明白为什么在CORS的上下文中,授权被视为“自定义标头”,而它显然是http协议的一部分。这意味着,在使用令牌身份验证的CORS场景中(我认为这在任何CORS场景中都是最佳实践),基本上每个请求,包括所有简单的GET请求,都将进行预检并因此需要更长的时间。 - Vincent Sels
1
@VincentSels 初始预检请求的结果可以由浏览器缓存,具体取决于响应头 Access-Control-Max-Age 的值。 - levi
@levi:那会很好,但请求URL必须与W3C CORS规范中定义的完全相同,包括URL参数:“url”字段值是请求URL的区分大小写匹配。这意味着在实践中,这些“get”预检请求几乎永远不会被缓存... - Vincent Sels

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接