跨域 & JSONP
同源策略
-
同源策略:浏览器出于安全性的考量而制定的策略,只允许与本域下的接口交互。> 不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。默认情况下,XHR对象只能访问与包含它的页面位于同一个域中的资源,即限制了来自不同源的"document"或脚本对当前"document"读取或设置某些属性。
-
它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。它限制了某个域下的文档或者js与另一个域中的资源交互的方式,它提供了一种安全机制,这种安全机制可以避免来自恶意网站的攻击。 同源策略要求浏览器当且仅当两个页面来自相同的域才允许其中一个网页上的js请求另一个网页的数据
-
URL(document.URL)由协议(http/https/file/ftp)、域名(doucment.domain)、端口(port:3000/4000/80/8080)、还有路径组成,如果两个URL的协议、域名、端口(URL组成部分前3个)相同,则表示他们同源。
- 协议类型(URI Schema)
- 相同域名(host name)
- 相同端口号(port number)
目的:
是出于安全性考虑,它能够阻止来自恶意网站的脚本通过其他网站的DOM获取其他网站的信息。可以避免CSRF和XSS攻击。
限制:
同源策略限制的是浏览器或者其他提供类似浏览器服务的软件,而且这仅仅是个规范;
同源策略只是限制JavaScript,而图片,css这些是不存在同源策略限制的。
跨域 & 跨域实现形式
跨域顾名思义就是突破同源策略的限制,去不同的域下访问数据。 在某网站的页面上通过js请求其他网站的数据,如两个网站不满足同源策略,那么该请求是为跨域.
- 如下几种实现形式:
- JSONP
- CORS
- 降域
- PostMessage
JSONP
首先在客户端声明一个callback, 然后把callback
的名字传给服务器。此时,服务器先生成 JSON
数据。然后以 javascript 语法的方式,生成一个function , function 名字就是传递上来的参数JSONP
.最后将 JSON
数据直接以入参的方式,放置到`function 中,这样就生成了一段 js 语法的文档,返回给客户端。客户端浏览器,解析script标签,并执行返回的 javascript 文档,此时数据作为参数,传入到了客户端预先定义好的 callback 函数里.(动态执行回调函数)
CORS
跨域资源共享( CORS )机制允许 Web 应用服务器进行跨域访问控制,从而使跨域数据传输得以安全进行。浏览器支持在 API 容器中(例如 XMLHttpRequest
或 Fetch )使用 CORS,以降低跨域 HTTP 请求所带来的风险。CORS 需要客户端和服务器同时支持。目前,所有浏览器都支持该机制。
- 使用XMLHttpRequest发送请求,如不同源,如后台有设置返回结果加入响应头: Access-Control-Allow-Origin;包含Origin的值...那么即可拿到响应数据..如无包含则被浏览器驳回...代码与Ajax一样.
Method.
1. JSONP:
<pre>
//在file协议下测试跨域 <script> $('.change').addEventListener('click', function() { var script = document.createElement('script'); //请求服务器地点 + callback=appendHtml(告诉服务器用此打包) script.src = 'http://localhost:8080/getNews?callback=appendHtml'; 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> //Server: app.get('/getNews', function(req, res){ var news = ['one','two','thr','fou','fiv','six','sev','eig','nin'] var data = [];//新数组 for(var i=0; i<3; i++){ //for循环遍历 var index = parseInt(Math.random()*news.length);//随机取0~7作为下标 data.push(news[index]);//推入 news.splice(index, 1);//news.length降一 } var cb = req.query.callback;// if(cb){ res.send(cb + '('+ JSON.stringify(data) + ')');//转换为一个JSON字符串,并发送 }else{ res.send(data); } })
</pre>
2. CORS:
<pre>
`
<script>
//监听事件
$('.change').addEventListener('click', function(){
//Ajax
var xhr = new XMLHttpRequest();
xhr.open('get', 'http://localhost:8080/getNews', true);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
appendHtml( JSON.parse(xhr.responseText) ) //使用JSON解析响应数据
}
}
})
function appendHtml(news){
var html = '';
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>';
}
$('.news').innerHTML = html;
}
function $(id){
return document.querySelector(id);
}
</script>
//Server:
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);
}
res.header("Access-Control-Allow-Origin", "http://AAA.com:8080"); //(只允许AAA的请求)
res.header("Access-Control-Allow-Origin", "*"); //允许任何请求(粗暴)
res.send(data);
})
`
</pre>
降域 _ Window.domain获取/设置当前文档的原始域部分, 用于 同源策略.
<pre>
//================a.html==================// <div class="main"> <input type="text" placeholder="......"> </div> <iframe src="a.b.com/b.html" frameborder="0" ></iframe> </div> <script> document.querySelector('.main input').addEventListener('input', function(){ console.log(this.value); window.frames[0].document.querySelector('input').value = this.value; }) document.domain = "b.com" </script> //================b.html==================// <script> document.querySelector('#input').addEventListener('input', function(){ window.parent.document.querySelector('input').value = this.value; }) document.domain = 'b.com'; 设置 </script>
</pre>
如果当前文档的域无法识别,那么domain属性会返回null。
在根域范围内,Mozilla允许你把domain属性的值设置为它的上一级域。例如,在 developer.mozilla.org 域内,可以把domain设置为 "mozilla.org" 但不能设置为 "mozilla.com" 或者"org"。
Mozilla 会区分 document.domain
属性 **从没有被设定过值 **和 被显示的设定为跟该文档的URL的domain一致的值,尽管这两种状况下,该属性会返回同样的值。两个文档,只有在 document.domain
都被设定为同一个值,表明他们打算协作;或者都没有设定 document.domain
属性并且URL的域是一致的 (如何判断一致),这两种条件下,一个文档才可以去访问另一个文档。如果不是因为这个特殊的策略,每一个站点都会成为他的子域的XSS攻击的对象(例如,https://bugzilla.mozilla.org 可以被来自 https://bug*.bugzilla.mozilla.org 站点的bug附件攻击)。
PostMessage
<pre>
`
//===============a.html================//
<div class="ct">
<h1>使用postMessage实现跨域</h1>
<div class="main">
<input type="text" placeholder="...">
</div>
<iframe src="a.abc.com" frameborder="0" ></iframe>//模拟的一个网址
</div>
<script>
$('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].postMessage(this.value,'*');
})
function $(id){
return document.querySelector(id);
}
</script>
//===============b.html================//
<script>
$('#input').addEventListener('input', function(){
window.parent.postMessage(this.value, '*');
})
window.addEventListener('message',function(e) {
$('#input').value = e.data
console.log(e.data);
});
function $(id){
return document.querySelector(id);
}
</script>
`
</pre>
-
JSONP
相对危险... -
CORS
把Access-Control-Allow-Origin关闭就不能访问了. - 降域 降了一级,达成同源
-
window.postMessage()
是指定给一个域名发送了请求,发送你想要的东西,你只需要接受就行了。当然你拒绝不要扔了也是可以的