我正在编写一个JavaScript客户端,可以包含在第三方网站中(类似Facebook的“赞”按钮)。它需要从需要基本HTTP身份验证的API中检索信息。简化的设置如下:
第三方站点在其页面上包含此代码段:
<script
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>
widget.js 调用了 API:
var el = document.getElementById('web-dev-widget'),
user = 'token',
pass = el.getAttribute('data-public-key'),
url = 'https://api.dev/',
httpRequest = new XMLHttpRequest(),
handler = function() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
console.log(httpRequest.responseText);
} else {
console.log('There was a problem with the request.', httpRequest);
}
}
};
httpRequest.open('GET', url, true, user, pass);
httpRequest.onreadystatechange = handler;
httpRequest.withCredentials = true;
httpRequest.send();
API已配置为响应适当的标题:
Header set Access-Control-Allow-Credentials: true
Header set Access-Control-Allow-Methods: "GET, OPTIONS"
Header set Access-Control-Allow-Headers: "origin, authorization, accept"
SetEnvIf Origin "http(s)?://(.+?\.[a-z]{3})$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
请注意,Access-Control-Allow-Origin
被设置为Origin
而不是使用通配符,因为我正在发送带凭据的请求(withCredentials
)。
现在一切就位,可以进行异步跨域身份验证请求,在OS X 10.8.2的Chrome 25中效果很好。在开发工具中,我可以看到网络请求GET
请求之前OPTIONS
请求,响应按预期返回。
在Firefox 19中进行测试时,Firebug中没有向API发送网络请求,并在控制台记录了以下错误:NS_ERROR_DOM_BAD_URI: Access to restricted URI denied
经过深入挖掘,我发现根据评论,Gecko不允许用户名和密码直接在跨站点URI中。我假设这是由于使用open()
的可选用户和密码参数,因此我尝试了另一种方法来进行身份验证请求,即将凭证进行Base64编码并在Authorization标头中发送:
// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);
...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);
这会导致OPTIONS
请求的401 Unauthorized
响应,引发像“为什么在Chrome中可以工作而Firefox不行?”这样的谷歌搜索!那时我知道自己遇到麻烦了。
为什么在Chrome中可以工作而Firefox不行?如何让OPTIONS
请求发送和响应始终一致?