1

一、什么是跨域?

跨域简单的理解就是JavaScript同源策略的限制。是出于安全的考虑,a.com域名下的js不能操作b.com或者c.com域名下的对象。
当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。不同域之间相互请求资源,就算叫“跨域”。

一个正常的域名地址组成(图片来自网络资源):
图片描述

注意:跨域不是请求发布出去,请求可以正常发出,服务器也能收到并返回结果,只是结果被浏览器所拦截了。

附上一张参考图,便于大家深入理解(图片来自网络资源)
图片描述

二、什么是同源策略与限制

同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。这是一种保护用户信息,防止恶意身份伪造的一种安全机制。
同源策略限制的内容:

  1. Ajax请求不能正常进行。
  2. Cookie、LocalStoage、indexDB等无法读取。

3.DOM 无法获得。

不过,有几个标签却可以允许跨域请求资源(可以作为解决跨域的一种方案)。

1.<img src="xxxx" alt="">
2.<link rel="stylesheet" href="xxx">
3.<script src="xxx"></script>

三、处理跨域方法一——JSONP

JSONP是跨域通信最常用的方法,其最大的特点就是简单适用、兼容性好,可用于解决主流浏览器的跨域数据访问的问题。
缺点是仅支持get方法具有局限性。
它的基本思想是,在网页中添加一个<script>标签,像服务器请求JSON数据。它需要服务器端的配合,服务器收到请求后,将数据放在指定名字的回调函数中传回来。

 <script type="text/javascript">
    function foo(data) {
        console.log(data.msg);
    }
</script>
<script type="text/javascript" src="http://xxx.com/xx?callback=foo"></script>

注意,该请求的查询字符串有一个callback参数,用来指定回调函数的名字,这对于JSONP是必需的。

由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

jQuery的jsonp形式

JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存

$.ajax({
url: "http://xxx/xx",
dataType: "jsonp",
type: "get",//可以省略
jsonpCallback: "fn",//->自定义传递给服务器的函数名,而不是使用jQuery自动生成的,可省略
jsonp: "jsonp",//->把传递函数名的那个形参callback变为jsonp,可省略
success: function (data) {
    console.log(data);
} });

四、处理跨域方法二——CORS

CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS允许任何类型的请求。缺点是兼容性不如JSONP。

CORS要求浏览器(>IE10)和服务器的同时支持,是跨域的根本解决方法,由浏览器自动完成。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
服务器端做的小改动:

header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Methods:POST,GET");

//在服务器端设置同源策略地址

router.get("/userlist",function (req, res,next) {
var user = {
        name: 'Mr.Cao',
        gender: 'male',
        career: 'IT Education'
    };
res.writeHeader(200, {"Access-Control-Allow-Origin": 'http://localhost:63342'});
res.write(JSON.stringify(user));
res.end(); });

在响应头上添加 Access-Control-Allow-Origin 属性,指定同源策略的地址。同源策略默认地址是网页的本身。只要浏览器检测到响应头带上了CORS,并且允许的源包括了本网站,那么就不会拦截请求响应。

五、处理跨域方法三——WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
下面是一个例子,浏览器发出的WebSocket请求的头信息

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

上面代码中,有一个字段是Origin,表示该请求的请求源(origin),即发自哪个域名。

正是因为有了Origin这个字段,所以WebSocket才没有实行同源政策。因为服务器可以根据这个字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应。

六、处理跨域方法四——跨文档通信 API/片段识别符/window.name

如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe窗口和window.open方法打开的窗口,它们与父窗口无法通信。

对于完全不同源的网站,目前有三种方法,可以解决跨域窗口的通信问题。

6.1 片段识别符

片段标识符(fragment identifier)指的是,URL的#号后面的部分,比如 http://xxx.com/x.html#fragmen...。如果只是改变片段标识符,页面不会重新刷新。
父窗口可以把信息,写入子窗口的片段标识符。

  var src = originURL + '#' + data;
 document.getElementById('myIFrame').src = src;

子窗口通过监听hashchange事件得到通知。

window.onhashchange = checkMessage;    
function checkMessage() {
  var message = window.location.hash;
  // ...
}

6.2 window.name
浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

父窗口先打开一个子窗口,载入一个不同源的网页,该网页将信息写入window.name属性。

window.name = data;

接着,子窗口跳回一个与主窗口同域的网址。

  location = 'http://parent.url.com/xxx.html';

然后,主窗口就可以读取子窗口的window.name了。

var data = document.getElementById('myFrame').contentWindow.name;

这种方法的优点是,window.name容量很大,可以放置非常长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

6.3 window.postMessage
上面两种方法都属于破解,HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API(Cross-document messaging)。
这个API为window对象新增了一个window.postMessage方法,允许跨窗口通信,不论这两个窗口是否同源。

举例来说,父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。

var popup = window.open('http://bbb.com', 'title');
popup.postMessage('Hello World!', 'http://bbb.com');

postMessage方法的第一个参数是具体的信息内容,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。也可以设为*,表示不限制域名,向所有窗口发送。
子窗口向父窗口发送消息的写法类似。

window.opener.postMessage('Nice to see you', 'http://aaa.com');

父窗口和子窗口都可以通过message事件,监听对方的消息。

window.addEventListener('message', function(e) {
  console.log(e.data);
},false);

message事件的事件对象event,提供以下三个属性。

event.source:发送消息的窗口
event.origin: 消息发向的网址
event.data: 消息内容

七 、处理跨域方法五-document.domain

Cookie 是服务器写入浏览器的一小段信息,只有同源的网页才能共享。但是,两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie。

举例来说,A网页是http://w1.example.com/a.html,B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie。

document.domain = 'example.com';

现在,A网页通过脚本设置一个 Cookie。

document.cookie = "test1=hello";

B网页就可以读到这个 Cookie。

var allCookie = document.cookie;

注意,这种方法只适用于 Cookie 和 iframe 窗口,LocalStorage 和 IndexDB 无法通过这种方法。


alisa
167 声望5 粉丝