前天面试被问到了跨域的问题,自我感觉回答的并不理想,下面我就分享一下整理后的总结分享给大家
一、为什么要跨域
安全限制
JavaScript或Cookie只能访问同域下的内容——同源策略
同源策略
下表相对于: http://h5.jd.com/dir/ajax.js
注意
- 协议和端口造成的跨域问题,非前端解决范畴
- 所谓域,是通过“url首部”来识别,而非判断域与ip的对应关系
(“URL的首部”指window.location.protocol +window.location.host)
二、跨域方案
1. jsonp
详见博客 JSON 和 JSONP两兄弟
2. cors
JSONP | CORS | |
目的 | 跨域 | 跨域 |
支持 |
get (受IE下url长度不能超过2083个字节的限制和出于安全考虑,一般不用来提交数据) |
所有类型的http请求 |
支持度 | 包括老式浏览器 | 不支持部分浏览器,移动端支持很好 |
缺点 |
1)安全问题(请求代码中可能存在安全隐患) 2)确定jsonp请求是否失败不太容易 3)只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题 |
支持率 |
原理 |
被包含在一个回调函数中的JSON 核心则是动态添加<script>标签来调用服务器提供的js脚本 (允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了) |
使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败 (只需由服务器发送一个响应标头即可) |
CORS需要浏览器和服务器同时支持
实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨域通信
1)两种请求方式
简单请求、非简单请求
a)简单请求:
跨域时,浏览器自动在头部信息中添加一个origin 字段(指定请求源-协议+域名+端口),如下图所示
服务器判断origin在域名许可范围内,返回响应:
若不存在 Access-Control-Allow-Origin 字段,则出错
以上头部信息中,CORS相关字段有
- Access-Control-Allow-Origin
必须字段,其值为 origin / *(可接受任意域名请求) - Access-Control-Allow-Credentials
可选,是否允许发送Cookie - Access-Control-Expose-Headers
可选,是否需要Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma之外的字段
withCredentials 属性
CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials
字段
Access-Control-Allow-Credentials: true
另一方面,开发者必须在AJAX请求中打开withCredentials
属性。
var xhr = new XMLHttpRequest(); xhr.withCredentials = true;
注意
如果要发送Cookie,Access-Control-Allow-Origin
就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie
也无法读取服务器域名下的Cookie。
b)非简单请求(不同时满足以上条件)
请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
浏览器对于非简单请求,就自动发出一个"预检"请求,要求服务器确认可以这样请求。下面是这个"预检"请求的HTTP头信息
除了Origin
字段,"预检"请求的头信息包括两个特殊字段。
- Access-Control-Request-Method
该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT
。
- Access-Control-Request-Headers
该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header
。
2.CORS 支持度
3. iframe
(只有在主域相同时才能使用)
1)www.a.com/a.html中:
document.domain = ‘a.com‘; var ifr = document.createElement(‘iframe‘); ifr.src = ‘http://www.script.a.com/b.html‘; ifr.display = none; document.body.appendChild(ifr); ifr.onload = function(){ var doc = ifr.contentDocument || ifr.contentWindow.document; //在这里操作doc,也就是b.html ifr.onload = null; };
2) 在www.script.a.com/b.html中:
document.domain = ‘a.com‘;
其他跨域方案
window.name:
在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。
window.postMessage:
该方法是 HTML5 新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
动态创建script
JSONP也就是利用这个原理。
利用iframe和location.hash
淘汰类技术
利用flash
淘汰类技术
参考链接:
http://tech.jandou.com/cross-domain.html
http://www.cnblogs.com/JChen666/p/3399951.html
http://www.ruanyifeng.com/blog/2016/04/cors.html