博主在项目开发中,遇到一个问题,在当前网页,发送一个跨域请求,可是浏览器给我报401:not authorizated。一开始以为是我写的http请求没有带入一些身份验证信息,后来仔细研究了下,此跨域请求没有带包含认证信息的cookie,可是我看了项目中别的http请求压根没有单独去设置这种cookie。
我又看了下Chrome—》 DevTools—》application—》Cookies,发现包含认证信息的cookie已经全部在当前域下了。说明我在发送请求时,并不需要自己在重新设置cookie值了;既然已经有cookie值,为什么我发送的请求的Request Headers里没有自动带上已经存在的cookie呢?
所以我在怀疑是不是因为我发送的请求时跨域的呢?
带着这个怀疑,我着重看了下CORS跨域相关文档。
一. CORS
Cross-Origin Resource Sharing 是 W3C 推出的一种跨站资源获取的机制。
首先我们来看一下浏览器的支持情况:
Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
4 | 3.5 | 8 & 9(XDomainRequest), 10 | 12 | 4 |
移动端的浏览器对这种方法的支持比较完善。
现在我们看到了,如果不需要兼容 IE6、7的话,就可以使用这种方法。
这种跨域方案主要的思想是:服务器 在响应头中设置相应的选项,浏览器如果支持这种方法的话就会将这种跨站资源请求视为合法,进而获取资源。
1.服务端可以设置的响应头信息:
(1) Access-Control-Allow-Origin
Access-Control-Allow-Origin: <origin> | *
origin: 被允许跨域访问这个资源的网站,* 代表全部网站。浏览器会检测这个参数,如果符合要求,才会去获取资源。
举个例子,允许 http://jasonkid.github.io/fezone 来跨域访问这个资源:
Access-Control-Allow-Origin: http://jasonkid.github.io/fezone
(2) Access-Control-Allow-Credentials
Access-Control-Allow-Credentials: true | false
表示是否允许浏览器携带 Cookie 来访问这个资源。
这个属性要和 XMLHttpRequest 的 withCredentials 属性来配合使用。var xhr = new XMLHttpRequest(); var url = 'http://foo.other/resources/credentialed-content/'; if(xhr) { xhr.open('GET', url, true); xhr.withCredentials = true; // 设置带有 Cookie 的资源请求 xhr.onreadystatechange = handler; xhr.send(); }
2.能够成功使用带有 Cookie 的资源请求需要满足以下几个条件:
(1) XMLHttpRequest 对象中指定了 withCredentials = true
(2) 服务器响应头中 Access-Control-Allow-Credentials: true
(3) 服务器响应头中 Access-Control-Allow-Origin 不能为 *
以下选项主要是安全性配置的问题,主要是服务器的配置问题了,就不展开介绍了:
Access-Control-Expose-Headers
Access-Control-Allow-Methods
Access-Control-Allow-Headers
所以,在发送跨域请求时之所以没能带上cookie,很有可能是因为ajax没有设置`withCredentials = true`这一属性。
那么继续研究下withCredentials属性是干啥吃的。。
二.withCredentials
查阅MSDN《火狐开发者文档》,发现有详细介绍:
XMLHttpRequest.withCredentials 属性是一个Boolean类型,它指示了是否该使用类似cookies,authorization headers(头部授权)或者TLS客户端证书这一类资格证书来创建一个跨站点访问控制(cross-site Access-Control)请求。在同一个站点下使用withCredentials属性是无效的。
此外,这个指示也会被用做响应中cookies 被忽视的标示。默认值是false。
如果在发送来自其他域的XMLHttpRequest请求之前,未设置withCredentials 为true,那么就不能为它自己的域设置cookie值。而通过设置withCredentials 为true获得的第三方cookies,将会依旧享受同源策略,因此不能被通过document.cookie或者从头部相应请求的脚本等访问。
注: 永远不会影响到同源请求
Note: 不同域下的XmlHttpRequest 相应,不论其Access-Control- header 设置什么值,都无法为它自身站点设置cookie值,除非它在请求之前将withCredentials 设为true。
实例
<code>
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://example.com/', true);
xhr.withCredentials = true;
xhr.send(null);
</code>
详见:
https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials
到此,已经很明白了,解决上述的问题就是需要设置xhr.withCredentials = true这一项。
但是,我是使用JQuery封装的ajax,所以需要借助$.ajax()方法的
xhrFields属性。(xhrFields是一个具有多个”字段名称-字段值”对的对象,用于对本地XHR对象进行设置。)
jQuery.ajax({ url: "https:" + _ENV_ + "api/1.0.0-beta/urlManager/getParameter", type:"get", contentType:"application/json", data:{ "md5":gs_all_params.gs_md5, "rt":new Date().getTime() }, xhrFields: { withCredentials: true }, success:function(response){ window[APP_ENV].gs_params =Object.assign(response.Parameter,gs_all_params) ; createApp(); }, error:function() { } });
运行后,不再报错并且得到了正确的结果。
总结:
出现这个问题,是因为本人对跨域请求带上原网站的cookie的相关知识不太了解,通过这次报错正好学习到了,因此记录下来供大家借鉴。