1: 什么是同源策略
最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页"同源",所谓"同源"指的是"三个相同".
协议相同
域名相同
端口相同举例来说:
http://www.example.com/dir/page.html
这个网址协议是http://
,域名是www.example.com
,端口是80
(默认端口可以省略),它的同源情况如下.
http://www.example.com/dir2/other.html
:同源
http://example.com/dir/other.html
:不同源(域名不同)
http://v2.www.example.com/dir/other.html
:不同源(域名不同)
http://www.example.com:81/dir/other.html
:不同源(端口不同)
https://www.example.com:81/dir/other.html
:不同源(https协议不同)
2: 什么是跨域?跨域有几种实现形式
-
跨域出现的原因
JavaScript出于安全方面的考虑,不允许一个网页访问一个非同源的网页,即2个网址的协议相同,域名相同,端口相同其中任意一个不同就是非同源. -
概念
:只要协议、域名、端口有任何一个不同,都被当作是不同的域。
比如
:有一个Ajax的var xhr=new XMLHttpRequest()的xhr对象,在a网址里发生请求到一个非同源的b网址,会请求的报错.
- 跨域就是为了解决这个问题,实现非同源网页之间的数据传输和通信.
跨域常见方式
JSONP
(JSON with Padding 填充式JSON 或参数式JSON)CORS
(Cross-Origin Resource Sharing,跨源资源共享)HTML5的
window.postMessage
window.postMessage(message,targetOrigin) 方法是html5新引进的特性,可以使用它来向其它的window对象发送消息,无论这个window对象是属于同源或不同源,目前IE8+、FireFox、Chrome、Opera等浏览器都已经支持window.postMessage方法。
window.postMessage允许两个窗口/帧之间跨域发送数据消息。从本质上讲,window.postMessage是一个跨域的无服务器垫片的Ajax。降域: document.domain
使用条件
:
有其他页面 window 对象的引用,
二级域名相同,
协议相同,
端口相同
//在页面 http://www.example.com/a.html 中设置document.domain:
<iframe src="example.com/b.html" id="iframe" onload="test()"></iframe>
<script>
document.domain='example.com';//设置成主域
function test(){
alert(document.getElementById('iframe').contentWindow);
}
</script>
//在页面 http://example.com/b.html中也设置document.domain
<script>
document.domain='example.com';//在iframe载入的这个页面也设置 document.domain与主页面相同
</script>
//而且是必须的,虽然这个文档的domain就是example.com,但是还是必须显示的设置document.domain的值
3: JSONP 的原理是什么
JSONP (JSON with Padding)是一个简单高效的跨域方式,html中的script标签可以加载并执行其他域的JavaScript,于是我们可以通过script标记来动态加载其他域的资源,例如我要从域A的页面pageA加载域B的数据,那么在域B的页面pageB中我以JavaScript的形式声明pageA需要的数据,然后在pageA中用script标签把pageB加载进来,那么pageB中的脚本就会得以执行。JSONP在此基础上加入了回调函数,pageB加载完之后会执行pageA中定义的函数,所需要的数据会以参数的形式传递给该函数。JSONP易于实现,但是也会存在一些安全隐患,如果第三方的脚本随意地执行,那么它就可以篡改页面内容,截获敏感数据。但是在受信任的双方传递数据,JSONP是非常合适的选择。
.css,.js,图片的引用和jsonp跨域拿到js方法有什么区别 ?
相同点
:
在html里.js,图片的引用和jsonp拿到js的方法是一样的,都是从服务器那到js文件然后插入到html里.
不同:
只不过出发点是不一样,前者是加载资源,后者为了跨域拿后台返回的js.
jsonp还多了一个回调,区别是出发点和应用方法不同,但都有从服务器返回的资源.
4: CORS是什么
CORS(Cross-Origin Resource Sharing)跨域资源共享,定义了必须在访问跨域资源时,浏览器与服务器应该如何沟通.CORS背后的基本思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败.跨域后浏览器不会返回数据.
**简单请求 **
浏览器将CORS请求分成两类:简单请求(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
凡是不同时满足上面两个条件,就属于非简单请求
-
使用
在服务器后台设置header属性Access-Control-Allow-Origin
它的值是请求时Origin字段的值或者*
,*
表示接受任意域名的请求。
app.get('/getNews', function(req, res){
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);
}
res.header("Access-Control-Allow-Origin", "http://a.jrg.com:8080"); //代表只接受http://a.jrg.com:8080网址的请求
//res.header("Access-Control-Allow-Origin", "*"); //*表示接受任意域名的请求
res.send(data);
})
-
非简单请求
是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight).
浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
-
CORS与JSONP的比较
CORS与JSONP的使用目的相同,但是比JSONP更强大,但CORS不支持IE6.7.8.
JSONP只支持GET请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据.
5: 演示三种常用以上跨域的解决方式
-
jsonp
参考阮一峰
先在客户端上设置 新 Hosts
127.0.0.1 a.jrg.com
127.0.0.1 b.jrg.com
127.0.0.1 jrg.com
router.js
app.get('/getNews', function(req, res){
var news = [
'我没有特别的才能,只有强烈的好奇心。永远保持好奇心的人是永远进步的人。——爱因斯坦',
'爱因斯坦认为他之所以取得成功,原因在于他具有狂热的好奇心.',
'求知欲,好奇心这是人的永恒的,不可改变的特性。哪里没有求知欲,哪里便没有学校。——苏霍姆林斯基',
'孩子提出的问题越多,那么他在童年早期认识周围的东西也就愈多,在学校中越聪明,眼睛愈明,记忆力愈敏锐。要培养自己孩子的智力,那你就得教给他思考。——苏霍姆林斯基',
'我想起了自己小学的学习经历,终于理解了为什么小时候成绩好,我那时候确实好奇心非常强烈.',
'人的内心里有一种根深蒂固的需要——总想感到自己是发现者、研究者、探寻者。在儿童的精神世界中,这种需求特别强烈。但如果不向这种需求提供养料,即不积极接触事实和现象,缺乏认识的乐趣,这种需求就会逐渐消失,求知兴趣也与之一道熄灭。(苏霍姆林斯基)',
'生活的全部意义在于无穷地探索尚未知道的东西,在于不断地增加更多的知识。——左拉'
]
var data = [];
for(var i=0; i<3; i++){
var index = parseInt(Math.random()*news.length);
data.push(news[index]);
news.splice(index, 1);//把data数组里已经有的元素从news数组里删除,保证不重复上一步拿到的新闻
}
var cb = req.query.callback
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}else{
res.send(data);
}
})
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
<li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
<li>我没有特别的才能,只有强烈的好奇心 ——爱因斯坦</li>
</ul>
<button class="change">点我换一组</button>
</div>
<script>
$('.change').addEventListener('click', function(){
var script = document.createElement('script');
script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml';//必须是'http://xxx.com:8080/的形式
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);//替换$(id)为document.querySelector(id),因为浏览器不支持jquery库
}
</script>
</body>
</html>
结合上面的案例说下jsonp的本质:
后台判断:
var cb = req.query.callback
if(cb){
res.send(cb + '('+ JSON.stringify(data) + ')');
}
场景:
A网访问跨域的B网资源:
在A网通过给script标签的src赋值一个网址.
例子: var script = document.createElement('script');
script.src = 'http://gaygay.com:8080/getNews?callback=appendHtml'
这个网址带有回调的方法名.加载script脚本到B网页,B网页发回消息去调用在A网页脚本里定义的回调函数.callback.
6.CORS案例
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>news</title>
<style>
.container{
width: 900px;
margin: 0 auto;
}
</style>
</head>
<body>
<div class="container">
<ul class="news">
<li>CORS练习</li>
<li>男双力争会师决赛 </li>
<li>女排将死磕巴西!</li>
</ul>
<button class="change">换一组</button>
</div>
<script>
$('.change').addEventListener('click', function(){
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://b.jrg.com:8080/getNews', true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
appendHtml( JSON.parse(xhr.responseText) )
}
}
window.xhr = xhr//why?
})
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>
</html>
router.js
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);
}
res.header("Access-Control-Allow-Origin", "http://jrg.com:8080");
//res.header("Access-Control-Allow-Origin", "*");
res.send(data);
7.document.domain降域
情景
:a.html里面嵌入iframe元素,且这个iframe是<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>
,而b.html就是个其中src规定显示在 iframe 中的文档的地址,也是绝对 URL - 指向其他站点(比如 src="www.example.com/index.html"),这里是个非同源的b.html
//a.html文件
<html>
<style>
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input{
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用降域实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://b.jrg.com:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "jrg.com"//降域关键代码
</script>
</html>
//b.html
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
document.querySelector('#input').addEventListener('input', function(){
window.parent.document.querySelector('input').value = this.value;
})
document.domain = 'jrg.com';
</script>
</html>
- window.frames[0].postMessage
//a.html文件
<html>
<style>
.ct{
width: 910px;
margin: auto;
}
.main{
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input{
margin: 20px;
width: 200px;
}
.iframe{
float: right;
}
iframe{
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
<div class="ct">
<h1>使用postMessage实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.jrg.com:8080/a.html">
</div>
<iframe src="http://localhost:8080/b.html" frameborder="0" ></iframe>
</div>
<script>
//URL: http://a.jrg.com:8080/a.html
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');//window.frames[0]是window子窗口的第一个.*代表任意地址
})
window.addEventListener('message',function(e) {
$('.main input').value = e.data
console.log(e.data);
});
//
window.addEventListener('message',function(e){
$('.mian input').value = e.data;
})
function $(id){
return document.querySelector(id);
}
</script>
</html>
//b.html文件:即被内嵌的iframe链接文档url.
<html>
<style>
html,body{
margin: 0;
}
input{
margin: 20px;
width: 200px;
}
</style>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
<script>
//传出数据到内嵌此窗口的父窗口即a.html.
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');//
//返回当前窗口的父窗口对象.如果一个窗口没有父窗口,则它的 parent 属性为自身的引用.
//如果当前窗口是一个 <iframe>, <object>, 或者 <frame>,则它的父窗口是嵌入它的那个窗口
})
//接收信息
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
</html>