主要内容: 同源策略、跨域实现形式(JSONP、CORS、降域、postMessage)
同源策略 (Same origin Policy)
浏览器处于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。
这是一个用于隔离潜在恶意文件的重要安全机制。
本域指的是:
- 同协议: 比如都是 http 或者都是 https,
- 同域名
- 同端口: 比如都是80端口(不加端口号,默认是80)
注意: 对于当前页面来说,页面存放的JS 文件的域不重要,重要的是加载该JS页面所在什么域(当前页面的URL和ajax请求的URL对比)。
跨域实现形式
跨域
只要协议、域名、端口中有任何一个不同,都被当做是不同的域。
跨域实现形式
JSONP
JSONP(JSON with padding)是资料格式 JSON 的一种“使用模式”,可以让网页从别的网域要资料。JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
JSONP 的原理:
JSONP是利用<script>标签没有跨域限制的“漏洞”来达到与第三方通讯的目的。
- 首先前端先设置好回调函数,并将其作为 url 的参数。
- 服务端接收到请求后,通过该参数获得回调函数名,并将数据放在参数中将其返回
- 收到结果后因为是 script 标签,所以浏览器会当做是脚本进行运行,从而达到跨域获取数据的目的。
index.html
<body>
<div class="ct">
<ul class="list">
</ul>
<button class="show"> showData </button>
</div>
<script>
var btn = document.querySelector('.show');
var news = document.querySelector('.list');
btn.addEventListener('click',function (){
var script = document.createElement('script');
script.src = 'http://127.0.0.1:8080/getData?callback=appendData';
document.body.appendChild(script);
document.body.removeChild(script)
});
function appendData(data){
var html = '';
for(var i=0;i<data.length;i++){
html += '<li>' + data[i] + '</li>'
}
news.innerHTML = html ;
}
</script>
</body>
server.js
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
var server = http.createServer(function (req, res) {
var pathObj = url.parse(req.url, true); // 将url字符串转换为URL对象
switch (pathObj.pathname) {
case '/getData':
var data = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心"
];
res.setHeader('Content-Type', 'text/json; charset=utf-8');
if (pathObj.query.callback) {
res.end(pathObj.query.callback + '(' + JSON.stringify(data) + ')')
} else {
res.end(JSON.stringify(data))
}
break;
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function (e, data) {
if (e) {
res.writeHead(404, 'not found');
res.end('<h1>404 Not Found</h1>')
} else {
res.end(data)
}
})
}
});
server.listen(8080);
运行 node server.js 在浏览器中打开 http://localhost:8080/index.html
JSONP的优点:
- 不受同源策略的限制
- 兼容性好
- 不需要 XMLHttpRequest 或 ActiveX 的支持;并且在请求完毕后可以通过调用 callback 的方式回传结果
JSONP的缺点 :
- 只支持GET请求而不支持POST等其它类型的HTTP请求
- 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面或 iframe 之间进行数据通信的问题
CORS
CORS 是一个 W3C 标准,全称是"跨域资源共享"(Cross-origin resource sharing)它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。CORS 需要浏览器和服务器同时支持才可以生效。
CORS 全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。 实现方式很简单,当使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以 CORS 的表象是让你觉得它与同源的 ajax 请求没啥区别,代码完全一样。
index.html
<body>
<ul class="list">
</ul>
<button class="show">showData</button>
<script>
var btn = document.querySelector('.show'),
list = document.querySelector('.list');
btn.addEventListener('click', function () {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://127.0.0.1:8080/getData', true);
xhr.send();
xhr.onload = function (){
console.log(xhr.responseText);
appendData(JSON.parse(xhr.responseText))
}
});
function appendData(data) {
var html = '';
for (var i = 0; i < data.length; i++) {
html += '<li>' + data[i] + '</li>'
}
list.innerHTML = html;
}
</script>
</body>
server.js
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
var server = http.createServer(function(req, res){
var pathObj = url.parse(req.url, true);
switch (pathObj.pathname) {
case '/getData':
var data = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心"
];
res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
// res.setHeader('Access-Control-Allow-Origin','*') // *表示接受所有域名的请求
res.end(JSON.stringify(data));
default:
fs.readFile(path.join(__dirname, pathObj.pathname), function(e, data) {
if (e) {
res.writeHead(404, 'not found');
res.end('<h1>404 Not Found</h1>')
} else {
res.end(data)
}
})
}
});
server.listen(8080);
还是运行 node server.js 在浏览器中打开 http://localhost:8080/index.html
CORS的优点:
- CORS支持所有类型的HTTP请求
- 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理
降域
此方案仅限主域相同,子域不同的跨域应用场景。
实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
在模仿之前,需要先修改一下hosts 。这里我增加了2条记录:
127.0.0.1 a.duan.com
127.0.0.1 b.duan.com
在浏览器中打开 http://a.duan.com:8080/a.html
a.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>降域 a.html</title>
<style>
.ct {
width: 910px;
margin: 0 auto;
}
.main {
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
iframe {
float: right;
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
</head>
<body>
<div class="ct">
<h1>使用降域实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.duan.com:8080/a.html">
</div>
<iframe src="http://b.duan.com:8080/b.html" frameborder="0"></iframe>
</div>
<script>
var input = document.querySelector('.main input');
input.addEventListener('input', function () {
console.log(this.value);
window.frames[0].input.value = this.value
});
document.domain = "duan.com"
</script>
</body>
</html>
b.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>降域 b.html</title>
<style>
html, body {
margin: 0;
}
input {
margin: 20px;
width: 200px;
}
</style>
</head>
<body>
<input type="text" placeholder="http://b.duan.com:8080/b.html">
<script>
var input = document.querySelector('input');
input.addEventListener('input', function () {
console.log(this.value);
window.parent.input.value = this.value
});
document.domain = "duan.com"
</script>
</body>
</html>
postMessage
postMessage 是 HTML5 新增加的一项功能,跨文档消息传输(Cross Document Messaging),目前:Chrome 2.0+、Internet Explorer 8.0+, Firefox 3.0+, Opera 9.6+, 和 Safari 4.0+ 都支持这项功能。
a.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>postMessage a.html</title>
<style>
.ct {
width: 910px;
margin: 0 auto;
}
.main {
float: left;
width: 450px;
height: 300px;
border: 1px solid #ccc;
}
.main input {
margin: 20px;
width: 200px;
}
iframe {
float: right;
width: 450px;
height: 300px;
border: 1px dashed #ccc;
}
</style>
</head>
<body>
<div class="ct">
<h1>使用postMessage实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.duan.com:8080/a.html">
</div>
<iframe src="http://b.duan.com:8080/b.html" frameborder="0"></iframe>
</div>
<script>
var input = document.querySelector('.main input');
input.addEventListener('input', function () {
console.log(this.value);
window.frames[0].postMessage(this.value,'*')
});
window.addEventListener('message',function(e){
input.value = e.data;
console.log(e.data)
})
</script>
</body>
</html>
b.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>postMessage b.html</title>
<style>
html, body {
margin: 0;
}
input {
margin: 20px;
width: 200px;
}
</style>
</head>
<body>
<input type="text" placeholder="http://b.duan.com:8080/b.html">
<script>
var input = document.querySelector('input');
input.addEventListener('input', function () {
console.log(this.value);
window.parent.postMessage(this.value,'*')
});
window.addEventListener('message',function(e){
input.value = e.data;
console.log(e.data)
})
</script>
</body>
</html>
在浏览器中打开 http://127.0.0.1:8080/a.html
参考: