跨域

同源限制策略

同源策略指的是:协议,域名,端口相同。同源策略是一种安全协议

对于普通的浏览器而言,上述三者只要有一个不同就会引发同源策略。从而限制他们之间的交互行为。具体限制如下:

  • Cookie、LocalStorage和IndexDB无法读取;
  • DOM无法获得;
  • AJAX请求不能发送。

简单来说,只要协议,域名,端口有任何一个的不同,就被当作是跨域

为什么要有同源限制

同源策略就是浏览器为了保证用户信息的安全,防止恶意的网站窃取数据,禁止不同域之间的JS进行交互,举两个例子:

比如一个黑客程序,他利用Iframe把真正的银行登录页面嵌到他的页面上,当你使用真实的用户名,密码登录时,他的页面就可以通过Javascript读取到你的表单中input中的内容,这样用户名,密码就轻松到手了。

1.用户访问www.mybank.com,登陆并进行网银操作,这时cookie啥的都生成并存放在浏览器;
2.用户突然想起件事,并迷迷糊糊的访问了一个邪恶的网站www.xiee.com;
3.如果这时浏览器不予限制,这时该网站就可以在它的页面中,没有限制地拿到银行的cookie;
4.此时还不加以限制,并且银行也没有做响应的安全处理的话,黑客就可以发起对www.mybank.com的请求,那么用户的信息有可能就这么泄露了。
5.而如果使用了同源策略,用户正常使用时访问的www.mybank.com网页和服务端在同一服务器,或者说两者没有触发同源策略,那么就可以安全正常访问。而黑客的网站,则来自于其他域名,触发了同源策略,首先他就无法读取cookie,就算读取了也无法发送ajax请求

其实,同源限制主要是用来防止 CSRF 攻击的。简单点说,CSRF 攻击是利用用户的登录态发起恶意请求。
也就是说,没有同源策略的情况下,A 网站可以被任意其他来源的 Ajax 访问到内容。如果你当前 A 网站还存在登录态,那么对方就可以通过 Ajax 获得你的任何信息。当然跨域并不能完全阻止 CSRF。

为什么要跨域

跨域确实一定程度上保障了客户信息的安全,但是对于像客户端和服务端不在同一个域名和服务器下这种情况,两者之间要实现交互,就需要进行跨域

跨域的解决办法

跨域资源共享(CORS)

CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通。CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

服务器端对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。

只需要在后台中加上响应头来允许域请求!在被请求的Response header中加入以下设置,就可以实现跨域访问了!

1
2
3
4
5
6
//指定允许其他域名访问
'Access-Control-Allow-Origin:*'//或指定域
//响应类型
'Access-Control-Allow-Methods:GET,POST'
//响应头设置
'Access-Control-Allow-Headers:x-requested-with,content-type'

CORS预检请求

虽然设置 CORS和前端没什么关系,但是通过这种方式解决跨域问题的话,就不得不认识一下预检请求,因为通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和复杂请求

什么是预检请求

preflight请求,就是在发生cors请求时,浏览器检测到跨域请求,会自动发出一个OPTIONS请求来检测本次请求是否被服务器接受。一个OPTIONS请求一般会携带下面两个与CORS相关的头:

  • Access-Control-Request-Method : 本次预检请求的请求方法。
  • Access-Control-Request-Headers:本次请求所携带的自定义首部字段。这些字段是导致产生OPTIONS请求的一个原因。

这样,服务端收到该预检请求后,会返回与CORS相关的响应头。主要会包括下面几个,但可能还会有其他的有关CORS字段:

  • Access-Control-Allow-Origin: 服务器允许的跨域请求源
  • Access-Control-Allow-Methods: 服务器允许的请求方法
  • Access-Control-Allow-Headers : 服务器允许的自定义的请求首部字段

需要注意的是:

1、在上面的两次请求中,预检请求只是一个检查的过程,它不会携带任何请求的参数;预检通过后的请求才会真正的携带请求参数与服务器进行数据通信。

2、若服务器对预检请求没有任何响应,那么浏览器不知道服务器是否支持CORS而不会发送后续的实际请求;或者服务器不支持当前的Origin跨域访问也不会发送后续请求。

发生预检请求的条件

上文提到过,发送请求时出现两种情况,分别为简单请求和复杂请求,而发生预检请求的条件:是否是简单请求。
简单请求则直接发送具体的请求而不会产生预检请求。满足下面的所有条件就不会产生预检请求,也就是该请求是简单请求:

  • 请求方法是GET、POST、HEAD其中任意一个
  • 必须是下面定义对CORS安全的首部字段集合,不能是集合之外的其他首部字段。
    Accept、Accept-Language、Content-Language、Content-Type、DPR、Downlink、Save-Data、Viewport-Width、Width。
  • Content-Type的值必须是text/plain、multipart/form-data、application/x-www-form-urlencoded中任意一个值

预检请求的例子

我使用的是nodejs作为后台
后端代码:

1
2
3
4
5
6
7
app.all('*', function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin','*');
res.setHeader('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials, X-PINGOTHER');
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});

前端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var xhr = new XMLHttpRequest();
var url = 'http://127.0.0.1:9093/login/status';

function callCors(){
if(xhr)
{
xhr.open('POST', url, true);
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send();
}
}
window.onload = function(){
callCors()
}

然后就可以在360浏览器里面看到两次请求(Chrome里面看不到预检请求,不知道为什么)
预检请求
在这里插入图片描述
正式请求
在这里插入图片描述
注意:要想在浏览器里面查看预检请求,必须勾选这个项目
在这里插入图片描述

jsonp跨域

JSONP 的原理很简单,就是利用 script 标签没有跨域限制的漏洞,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的。

通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入。所以jsonp是需要服务器端的页面进行相应的配合的。(即用JavaScript动态加载一个script文件,同时定义一个callback函数给script执行而已。)

1
2
3
4
5
6
<script src="http://domain/api?param1=a&param2=b&callback=jsonp"></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>

jsonp跨域优缺点

优点:

  • 对于老的浏览器兼容性不错
  • 不需要浏览器(上古浏览器)支持XMLHttpRequest

缺点:

  • 只支持GET请求,不支持POST请求
  • 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。

document.domin配合iframe跨域

两个网页一级域名相同,只是二级域名不同,浏览器允许通过设置document.domain共享 Cookie或者处理iframe
只需要给页面添加 document.domain = ‘test.com’ 表示主域名都相同就可以实现跨域
在这里插入图片描述

window.name + iframe跨域

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。

比如:有一个页面a.html,它里面有这样的代码:

1
2
3
4
window.name = "我是a页面设置的";
setTimeout(function(){
window.location = "http://127.0.0.1/JSONP/b.html";
},1000)

b.html页面的代码:

1
console.log(window.name);

a.html页面载入后1秒,跳转到了b.html页面,结果b页面打印出了:

我是a页面设置的

可以看到在b.html页面上成功获取到了它的上一个页面a.html给window.name设置的值。如果在之后所有载入的页面都没对window.name进行修改的话,那么所有这些页面获取到的window.name的值都是a.html页面设置的那个值。当然,如果有需要,其中的任何一个页面都可以对window.name的值进行修改。

注意,window.name的值只能是字符串的形式,这个字符串的大小最大能允许2M左右甚至更大的一个容量,具体取决于不同的浏览器,但一般是够用了。

那么在a.html页面中,我们具体怎么获取数据呢,因为我们想要即使a.html页面不跳转也能得到b.html里的数据,而不是只能页面跳转才获取。答案就是在a.html页面中使用一个隐藏的iframe来充当一个中间人角色,由iframe去跳转获取b.html的数据,然后a.html再去得到iframe获取到的数据。

postMessage跨域

这种方式通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息

1.)a.html:(http://www.domain1.com/a.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://www.domain2.com');
};

// 接受domain2返回数据
window.addEventListener('message', function(e) {
alert('data from domain2 ---> ' + e.data);
}, false);
</script>

2.)b.html:(http://www.domain2.com/b.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script>
// 接收domain1的数据
window.addEventListener('message', function(e) {
alert('data from domain1 ---> ' + e.data);

var data = JSON.parse(e.data);
if (data) {
data.number = 16;

// 处理后再发回domain1
window.parent.postMessage(JSON.stringify(data), 'http://www.domain1.com');
}
}, false);
</script>
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2021 AURORA_ZXH
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信