这个问题是自己在升级答辩的时候被问到的,由于自己当时的基础知识太过薄弱,所以这个问题并没有完整的回答到位,当时候就对这个问题进行过简单的整理,但是发现还是没有理解深刻,今天在看书的时候又再碰到,现在又一次的来看到这个问题,不知道自己是不是会有一些新的体会。
什么是跨域
Ajax通信是通过XHR实现的,XHR对象只能访问与包含它的页面位于同一域中的资料。这个就是同源策略,同一来源指的是主机名、协议和端口号的组合。
CORS
CORS(Cross-Origin Resource Sharing,跨域资源共享)定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。使用自定义的HTTP头部让浏览器与服务器进行沟通。
只要我们对需要访问的服务器的response header进行设置就可以跨域了,这样是为了确定该服务器的返回到该页面是安全的。
// 设置header的含义
Access-Control-Allow-Origin:与简单的请求相同
Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔
Access-Control-Allow-Header:允许的头部,多个头部以逗号分隔
// 最简单的设置
res.header("Access-Control-Allow-Origin", "*");
最后一种最简单的设置,就是让所有域都认为当前访问是安全的。如果要设置百度访问是安全的设置可以是res.header("Access-Control-Allow-Origin", "http://www.baidu.com");
IE8的支持
IE8的支持方式是引入XDR类型,这个对象与XHR类似,但能实现安全可靠的跨域通信。XDR的访问也会有一些问题:
- cookie 不会随请求发送,也不会随响应返回。
- 只能设置请求头部信息中的 Content-Type 字段
- 不能访问响应头部信息
- 只支持 GET 和 POST 请求
使用方法和xhr类似:
var xdr = new XDomainRequest();
xdr.open("get", "http://192.168.199.128:3333?a=1&b=2");
xdr.onload = function(){
alert(xdr.responseText)
}
xdr.send();
其他浏览器的实现
其他浏览器都使用XMLHttpRequest对象实现了对CORS的原生支持,这就意味着我们不需要写其他的代码。同样这些也会有相应的限制
- 不能使用 setRequestHeader() 设置自定义头部
- 不能发送和接受 cookie
- 调用 getAllResponseHeaders() 方法总会返回空字符串
// 前端
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
xhr.open('get', 'http://192.168.199.128:3333?a=1&b=2', true);
xhr.send(null)
// 后端
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'application/json',"Access-Control-Allow-Origin": "*"});
console.log(new Buffer('success'))
res.write(new Buffer('success'));
res.end();
}).listen(3333)
带凭据的请求
什么是凭证,官方的解释是:默认情况下,跨源请求不提供凭据(cookie、HTTP认证及客户端SSL证明等),通过将withCredentials属性设置为true,可以指定某个请求应该发送凭据。个人也觉得好难解释,而且觉得这个的用处是什么?仅仅能证明,你是你么?
还是讲用法吧,如果请求中带了这个凭证的话,需要在服务器端也设置这么一个东西。res.header("Access-Control-Allow-Credentials", true);这样就可以达到消息等价了。
// 前端
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.onreadystatechange = function(){
if(xhr.readyState == 4){
alert(xhr.responseText);
}
}
xhr.open('get', 'http://192.168.199.128:3333?a=1&b=2', true);
xhr.send(null)
// 后端
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials', true,
'Access-Control-Allow-Origin": "*',
});
console.log(new Buffer('success'))
res.write(new Buffer('success'));
res.end();
}).listen(3333)
图像Ping
<img>标签的请求是可以跨域的,这个是最简单的单向跨域通信的一种方式。一般用这种方式来发统计那些,其实就是一个简单的GET请求。
缺点:单向且无法得到响应文本。
// 前端
var img = new Image();
img.onload = function(){
alert("Done");
}
img.onerror = function(){
alert("error");
}
img.src = 'http://127.0.0.1:3333?a=1&b=2'
// 后端
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200);
res.end();
}).listen(3333)
JSONP
JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
这个应该是目前最为流行的一种跨域方式,简单易用,本质上和<img>是差不多的原理,通过动态的添加<script>标签来使用,src可以指定跨域的url。但是<script>标签是有效的javascript代码,所以在请求完成后,即在JSONP响应加载到页面中以后,就会立即执行。
// 前端
function sayHello(a,b){
alert('name:' + a + ';age:' + b);
}
var script = document.createElement('script');
script.src = "http://127.0.0.1:3333?callback=sayHello";
document.body.insertBefore(script, document.body.firstChild);
// node 服务端
var http = require('http');
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'application/json',"Access-Control-Allow-Origin": "*"});
res.write('sayHello("karyn","23")');
res.end();
}).listen(3333)
缺点:不安全。不能对请求的成功与否进行监听,目前的方式一般都是计时器去判断有没有接收到响应。
以上就是不同源中文档的跨域解决的方案。
当然还有限制浏览器中不同域的框架之间的js交互的,就是iframe之间的数据交互。对此也有一些方案。这里就不对此进行详细介绍了
- 通过修改document.domain来跨子域
- 使用window.name来进行跨域
- 使用HTML5中新引进的window.postMessage方法来跨域传送数据
- 利用剪贴板