一、 跨域的概念
同源策略(Same origin Policy):
同源策略是指浏览器处于安全考虑,只允许与本域下的接口进行交互,不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。同源(本域)的概念:
同协议:都是为http或者https;
同域名:http://a.com/index.html 与 http://a.com/server.js 域名一致;
同端口:端口号一致(如都为8080)。跨域就是因为浏览器的同源策略机制存在而产生的,跨域的几种形式:
- http://www.abc.com/index.html 调用 http://www.abc.com/server.php
(同源,非跨域,协议、域名及端口号都一致)- http://www.abc.com/index.html 调用 http://www.def.com/server.php
(主域名不同,跨域)- http://abc.a.com/index.html 调用 http://def.a.com/server.php
(子域名不同:abc/def,跨域)- http://www.abc.com:8080/index.html 调用 http://www.abc.com:8081/server.php
(端口不同,跨域)- http://www.abc.com/index.html 调用 https://www.abc.com/server.php
(协议不同:http/https,跨域)
需要注意的是:对于当前页面来说页面存放的 JS 文件的域不重要,重要的是加载该 JS 页面所在什么域。
二、跨域的四种实现形式
1. JSONP
JSONP(JSON with padding)原理:
利用html里面script标签可以加载其他域下的js这一特性,使用script src的形式来获取其他域下的数据,但是因为是通过标签引入的,所以会将请求到的JSON格式的数据作为js去运行处理,显然这样运行是不行的,所以就需要提前将返回的数据包装一下,封装成函数进行运行处理,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。(JSONP 需要对应接口的后端的配合才能实现)实例:
<script>
function showData(ret){
console.log(ret);
}
</script>
<script src="http://api.jirengu.com/weather.php?callback=showData"></script>
当script src请求到达后端后,后端会去解析callback这个参数获取到字符串showData,在发送数据后端返回数据用showData封装一下,即 showData({"json数据"}) ,前端script标签在加载数据后会把json数据作为showData的参数,调用函数运行。
2. CORS
CORS 的概念:
CORS全称是跨域资源共享(Cross-Origin Resource Sharing),是一种 ajax 跨域请求资源的方式,支持现代浏览器,IE支持10以上。实现方式:
当使用 XMLHttpRequest 发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头:Origin,后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头:Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含 Origin 的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。实例:
server.js
var http = require('http')
var fs = require('fs')
var path = require('path')
var url = require('url')
http.createServer(function(req, res){
var pathObj = url.parse(req.url, true)
switch (pathObj.pathname) {
case '/getNews':
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米羽球",
"正直播柴飚/洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心"
]
res.setHeader('Access-Control-Allow-Origin','http://localhost:8080')
//res.setHeader('Access-Control-Allow-Origin','*')
res.end(JSON.stringify(news))
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)
}
})
}
}).listen(8080)
index.html
<!DOCTYPE html>
<html>
<body>
<div class="container">
<ul class="news"></ul>
<button class="show">show news</button>
</div>
<script>
$('.show').addEventListener('click', function(){
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://127.0.0.1:8080/getNews', true)
xhr.send()
xhr.onload = function(){
appendHtml(JSON.parse(xhr.responseText))
}
})
function appendHtml(news){
var html = ''
for( var i=0; i<news.length; i++){
html += '<li>' + news[i] + '</li>'
}
$('.news').innerHTML = html
}
function $(selector){
return document.querySelector(selector)
}
</script>
</html>
3. 降域(前提主域名要一致)
http://a.yilia.com 调用http://b.yilia.com
<script>
document.domain = yilia.com;
</script>
//将两个域名都降域,此时就可以相互访问了
4. postMessage
假设有两个域名(主域域名不一致),其中iframe页面是允许访问调用,那么就可以用postMessage实现。
原理:
a域名发送请求postMessage,b域名间听到了message事件,就处理并返回数据
//b域名
<script>
window.frames[0].postMessage(this.value, '*');
//*号表示在任何域下都可以接收message
window.addEventListener('message', function(e){
console.log(e.data);
});
</script>