何为跨域?
一般情况下,XMLHttpRequest(XHR)对象只能访问与包含它的页面位于同一个域中的资源,这种安全策略可以预防某些恶意行为,即通常所说的同源策略。
只要协议、域名、端口号有任何一个不同,都会被当成不同的域。
下表给出了相对于 http://store.company.com/dir/... 同源检测的结果:
跨域解决方法
JSONP(JSON with padding)
JSONP由两部分组成:回调函数和数据。回调函数时当响应到来是应该在页面中调用的函数,名字一般在请求中指定。而数据就是传入回调函数中的JSON数据。
JSONP通过动态创建<script>标签来使用,script 的 src 属性不受同源策略的约束。
JSONP原理:
- 首先在客户端注册一个callback, 然后把callback的名字传给服务器。
- 此时,服务器先生成 json 数据。然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数 。
- 最后将 json 数据直接以入参的方式,放置到 function 中,这样就生成了一段 js 语法的文档,返回给客户端。
- 客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)
下面例子通过查询地理定位服务来显示IP地址及位置信息。
function handleResponse(res){
alert("You are at IP address" + res.ip + ",which is in" + res.city + ',' + res.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script,document.body.firstChild);
JSONP优点:
- 兼容所有的浏览器
JSONP缺点:
- JSONP 是从其他域中加载代码执行。如果其他域不安全,很可能会在响应中夹带一些恶意代码,而此时除了完全放弃 JSONP 调用之外,没有办法追究。
- 要确定 JSONP 请求是否失败并不容易。如果动态脚本插入有效,就执行调用;如果无效,就静默失败。失败是没有任何提示的。
- 只支持 get 请求。
跨源资源共享(CORS:Corss-Origin Resource Sharing)
CORS需要浏览器和服务器同时支持。在整个请求过程中,都是由浏览器自动完成,不需要用户参与。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加头信息,有时还会多出一次附加请求。
浏览器将CORS请求分成两类:
简单请求(simple request)和非简单请求(not-so-simple request)。
简单请求需要同时满足下面两个条件:
- 请求方法只能是HEAD/GET/POST三个之一;
- 请求头的Content-Type只能是application/x-www-form-urlencoded,multipart/form-data,text/plain三个之一
凡是不同时满足以上两个条件的,就是非简单请求。
简单请求
对于简单请求,浏览器在请求头信息中增加一个Origin字段,用来说明本次请求来自哪个源(协议+域名+端口),服务器根据这个值来决定是否同意这次请求。
如果Origin指定的源在许可范围内,服务器返回响应的头信息会增加如下几个字段:
Access-Control-Allow-Origin:必须, 表明可以同意哪些跨源请求,设置为*表明同意任意跨源请求
Access-Control-Allow-Credentials:可选,布尔值,表明服务器是否允许浏览器携带用户凭证相关信息,例如cookie、http认证等信息。CORS请求默认不发送Cookie和HTTP认证信息,如果要发送需要将 withCredentials属性设置为true。 如果服务器不返回该字段,即不允许,此时若浏览器发送了一个withCredentials属性为true的请求,则会被拒绝,在控制台报错。
非简单请求
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),询问当前网页所在的域名是否在服务器的许可名单之中,以及可以使用那些http方法及头信息字段。只有得到肯定的答复,浏览器才会正式发出XMLHTTPRequest请求。
预检请求使用的http方法是Options,表示这是个用来询问的请求。 http请求头中除了包含简单请求头中的origin字段,还包含几个特殊字段:
Access-Control-Request-Method:表明请求使用的HTTP方法
Access-Control-Request-Header:用于在预先请求时,告知服务器要发起的跨域请求中会携带的请求头信息,是一个以逗号分隔的字符串
如果服务器确认允许跨源请求,则可以做出回应
预检请求的回应除了简单请求的回应中的Access-Control-Allow-Origin字段外,还有几个特殊字段:
Access-Control-Allow-Methods:表明服务器支持的所有跨域请求的方法,返回所有可以避免多次预检请求
Access-Control-Allow-Headers:表明跨域资源可以携带的自定义表头列表
Access-Control-Max-Age:表明浏览器可以将相应结果进行缓存的时间(单位为秒),在缓存时间内可以不用重复发送预检请求
如果服务器否定了预检请求,会返回一个正常的HTTP相应,但是没有任何有关CORS的头信息字段。这时浏览器就会认定服务器不同意预检请求,会触发一个错误,在控制台报错.
只要服务器通过了预检请求,以后每次正常的CORS请求,就跟简单请求一样。
CORS优点:
- 相比于JSONP,不仅支持get请求,也支持post、put等请求。
CORS缺点:
- 不是所有的浏览器都支持
- 需要服务器支持
CORS浏览器兼容情况:
WebSocket
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工双向通讯的协议,同源策略对 Web Sockets 不适用,因此可以实现异步跨域请求。
为了建立一个 WebSocket 连接,客户端浏览器首先要向服务器发起一个 HTTP 请求,这个请求和通常的 HTTP 请求不同,包含了一些附加头信息,其中附加头信息"Upgrade: WebSocket"表明这是一个申请协议升级的 HTTP 请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的 WebSocket 连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在直到客户端或者服务器端的某一方主动的关闭连接。
客户端在收到服务器响应后,建立的连接会从 HTTP 协议交换为 Web Socket 协议。标准的 HTTP 无法实现 Web Sockets,只有支持这种协议的专门服务器才能正常工作。 http:// => ws://、https:// => wss://使用自定义协议的好处是在服务器和客户端之间发送的数据量小,不必担心 HTTP 那样字节级的开销,比较适合移动通信及对实时性要求较高的应用。
// 要创建 Web Socket,需要先实例一个 WebSocket 对象并传入服务器端的地址,必须为一个绝对URL:
var socket = new WebSocket('ws://echo.websocket.org');
// 当浏览器和服务器连接成功后,会触发 open 事件
socket.onopen = function(evevt){
console.log('open');
}
// 当服务器向客户端发来消息时,会触发 message 事件
socket.onmessage = function(event){
var data = event.data; // 返回的数据是字符串
console.log(data);
}
// 当浏览器收到服务器发送的关闭连接请求时,就会触发 close 事件
socket.onclose = function(event){
console.log('close');
}
// 如果连接失败,发送、接收数据失败或者处理数据出现错误,会触发 error 事件
socket.onerror = function(event){
console.log('error');
}
// 使用send方法像服务器发送数据(只能发送纯文本数据,复杂的数据结构需要JSON.stringify一下):
socket.send("Hello World");
注:WebSocket对象不支持DOM2级事件侦听器,必须使用DOM0级事件语法。
WebSocket缺点:
- 不是所有的服务器都支持
- 必须有相应的服务器支持。
WebSocket优点:
- 支持实时通信,可以避免浏览器的短轮询及服务器的长轮询。
- 长连接会带来一定的服务器内存开销
WebSocket浏览器兼容情况:
参考文档
- 《javascript 高级程序设计》第21章 跨源资源共享
- 阮一峰:跨域资源共享CORS详解
- CORS测试网站:http://test-cors.org
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。