浏览器安全的基石是"同源政策"。所谓"同源"指的是"三个相同"。
- 协议相同
- 域名相同
- 端口相同
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。虽然这些限制是必要的,但是有时很不方便,合理的用途也受到影响。所以引出跨域的几种解决方式。如有错误请指正。
首先对于ajax的请求不能发送:
- JSONP
- CROS
- JSONP
JSONP是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,老式浏览器全部支持,服务器改造非常小。
它的基本思想是,网页通过添加一个<script>
元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
$('.change').addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://127.0.0.1:8080/getNews?callback=appendHtml';
//向服务器http://127.0.0.1:8080发送请求,请求的查询字符串有一个callback参数,用来指定回调函数的名字
document.head.appendChild(script);
//服务器收到请求后,将数据放在回调函数的参数位置返回
document.head.removeChild(script);
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(id){
return document.querySelector(id);
}
上面代码通过动态添加<script>
元素,向服务器http://127.0.0.1:8080发出请求。注意,该请求的查询字符串有一个callback
参数,用来指定回调函数的名字,这对于JSONP是必需的。
由于<script>
元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了appendHtml函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse
的步骤。
服务器收到请求后,将数据传给回调函数返回
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心",
"没有中国选手和巨星的110米栏 我们还看吗?",
"中英上演奥运金牌大战",
"博彩赔率挺中国夺回第二纽约时报:中国因对手服禁药而丢失的奖牌最多",
"最“出柜”奥运?同性之爱闪耀里约",
"下跪拜谢与洪荒之力一样 都是真情流露"
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);
}
var cb = req.query.callback;
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}else{
res.send(data);
}
})
- CORS
CORS
是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET
请求,CORS允许任何类型的请求。CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。
- 简单请求
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://b.hth:8080/getNews', true);
xhr.send();
//xhr.open的传的url为绝对路径,也就是你要跨域访问的接口地址
服务器端:
res.header("Access-Control-Allow-Origin", "http://a.hth:8080");
//指定可以请求数据的域名为http://a.hth:8080
- 非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest
请求,否则就报错。
var url = 'http://api.alice.com/cors';
var xhr = new XMLHttpRequest();
xhr.open('PUT', url, true);
xhr.setRequestHeader('X-Custom-Header', 'value');
xhr.send();
上面代码中,HTTP请求的方法是PUT
,并且发送一个自定义头信息X-Custom-Header
。
下面设置请求回应设置http的头部Access-Control-Allow-Origin
为*
,表示同意任意跨源请求。
res.header("Access-Control-Allow-Origin", "*");
iframe
如果两个网页不同源,就无法拿到对方的DOM。典型的例子是iframe
窗口和window.open
方法打开的窗口,它们与父窗口无法通信。
- 降域:
如果两个窗口一级域名相同,只是二级域名不同,那么设置上一节介绍的document.domain
属性,就可以规避同源政策,拿到DOM。 - window.postMessage
HTML5为了解决这个问题,引入了一个全新的API:跨文档通信 API
window.frames[0].postMessage(this.value,'*');//发送消息
window.parent.postMessage(this.value, '*');
参考链接: