跨域
修改本机host
- 以管理员身份进入CMD。右键开始菜单,选择“命令提示符(管理员)(A)。
- 输入命令“cd C:\Windows\System32\Drivers\etc",回车,进入hosts文件目录。
- 输入命令“notepad hosts",回车,用记事本打开hosts。
- 修改对应内容后保存即可。
同源策略(some origin policy)
- 浏览器出于安全方面的考虑,只允许与本域下的接口交互。不同源的客户端脚本在没有明确授权的情况下,不能读写对方的资源。(在开发时,可以通过禁止浏览器的安全策略来达到不同域下的交互)。
本域指的是:
- 同协议:如都是htt或者https
- 同域名:如都是http://jirengu.com/a和http://jirengu.com/b
- 同端口:如都是80端口如:htp://irengu.com/a/b.js和htp://jirengu.com/index.php (同源)
不同源的例子:
htp://jirengu.com/main.js 和htp://bbs.jirengu.com/a.php (域名不同,域名必须完全相同才可)
hp://jingu.com/main.js和htpt/:/irengu.com:8080/a.php (端口不同,第一 个是80;浏览器打开页面时,默认的是80端口)
需要注意的是:对于当前页面来说页面存放的JS文件的域不重要,重要的是加载该JS页面所在什么域在同源策略问题上,事实上请求已经发到服务器上,服务器也已经给了响应,是浏览器的安全机制觉得该文件不安全,对该文件不接受。可以通过修改浏览器的安全机制来解决该问题,但是不适用于用户使用,多数用于开发调试等。就算是相对应的域名,浏览器也是不予以支持的,必须是一模一样的。
例如:
localhost对应了127.0.0.1,但是在对于同源策略问题上,二者仍然是不同的,浏览器不予以支持。
跨域的几种方法
JSONP:
- HTML的的script标签可以使用任何页面对应的文件,只要它愿意。
可以先这样试试:
<script src="http://api.j irengu.com/weather.php"></script>
这时候会向天气接口发送请求获取数据,获取数据后做为js来执行。但这里有个问题,数据是 JSON格式的数据,直接作为JS运行的话我如何去得到这个数据来操作呢?
这样试试:
<script src="http://api.j irengu.com/weather.php?callback=showData"></script>
这个请求到达后端后,后端会去解析callback这个参数获取到字符串showData,在发送数据做如下处理:之前后端返回数据: {"city": "hangzhou", "weather": "晴天"}现在后端返回数据: showData({"city":"hangzhou", "weather": “晴天"})前端script标签在加载数据后会把[showData({"city": “hangzhou","weather":“晴天")]做为js来执行,这实际. 上就是调用showData这个函数,同时参数是{"city":"hangzhou",“weather":“晴天”}。用户只需要在加载提前在页面定义好showData这个全局函数,在函数内部处理参数即可。 - index.html:
<body>
<div class="container">
<ul class="news">
</ul>
<button class="show">show news</button>
</div>
</body>
<script>
$('.show').addEventListener('click',function() {
var script = document.createElement('script');
script.src = 'http://127.0.0.1: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 $(selector) {
return document.querySelector(selector);
}
</script>
- server.js
/* 跨域 */
var http = require('http')
var fs = require('fs')
var url = require('url')
var path = require('path')
http.createServer(function(req,res) {
var pathObj = url.parse(req.url,true);
/* console.log(pathObj) */
switch (pathObj.pathname) {
case '/getNews':
var news = [
"第11日前瞻:中国冲击4金 博尔特再战200米",
"正在直播柴飚//洪炜出战 男双力争会师决赛",
"女排将死磕巴西!郎平安排男陪练模仿对方核心"
]
res.setHeader('content-Type','text/json;charset=utf-8')//用于告诉浏览器编码方式,以防出现乱码。写在请求头中。
if(pathObj.query.callback) {
res.end(pathObj.query.callback + '(' + JSON.stringify(news) + ')');
}else{
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)
- 这就是JSONP(JSON with padding),总结一下:JSONP是通过script标签加载数据的方式去获取数据当做JS代码来执行提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP需要对应接口的后端的配合才能实现。
res.setHeader('content-Type','text/json;charset=utf-8')
- 上述代码,用于告诉浏览器编码方式,以防出现乱码。写在请求头中。
- JSONP跨域不像下面的CORS跨域那样受同源政策的影响,而且兼容性也比较好,但JSONP跨域也有其缺点,主要表现在:
- 它支持 GET 请求而不支持 POST 等其它类行的 HTTP 请求。
- 它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面或 iframe 之间进行数据通信的问题
- JSONP从其他域中加载代码执行,如果该域不安全并且夹带一些恶意代码,会存在安全隐患
- 要确定JSONP请求是否失败并不容易
CORS:
- CORS全称是跨域资源共享(Cross-Origin Resource Sharing),是一种ajax跨域请求资源的方式,支持现代浏览器,IE支持10以上。实现方式很简单, 当你使用XMLHttpRequest发送请求时,浏览器发现该请求不符合同源策略,会给该请求加一个请求头: Origin(其中包含了host), 后台进行一系列处理,如果确定接受请求则在返回结果中加入一个响应头: Access-Control-Allow-Origin; 浏览器判断该相应头中是否包含Origin的值,如果有则浏览器会处理响应,我们就可以拿到响应数据,如果不包含浏览器直接驳回,这时我们无法拿到响应数据。所以CORS的表象是让你觉得它与同源的ajax 请求没啥区别,代码完全一样。
= index.html:
<body>
<div class="container">
<ul class="news">
</ul>
<button class="show">show news</button>
</div>
</body>
<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>';
}
console.log(html);
$('.news').innerHTML = html;
}
function $(selector) {
return document.querySelector(selector);
}
</script>
- server.js:
/* 跨域 */
var http = require('http')
var fs = require('fs')
var url = require('url')
var path = require('path')
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.setHeader('content-Type','text/json;charset=utf-8')
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)
- CORS 的优缺点:
- 使用简单方便,更为安全
- 支持 POST 请求方式
- CORS 是一种新型的跨域问题的解决方案,存在兼容问题,仅支持 IE 10 以上
降域:
- 当两个页面不同域,但是它们的父域之上都相同(端口),那么可以使用降域的方法来实现跨域。
- 对于主域相同而子域不同的情况下,可以通过设置 document.domain 的办法来解决,具体做法是可以在url为a.jrg.com:8080下的a.html 和b.jrg.com:8080下的b.html 两个文件分别加上 document.domain = "jrg.com";然后通过 a.html 文件创建一个 iframe,去控制 iframe 的 window,从而进行交互,当然这种方法只能解决主域相同而二级域名不同的情况。
- 对于这种在不同域下虽然可以获取该页面,但是外部的js无法获取和操作该页面,这也是浏览器的安全机制之一。
- a.html:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<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>
</head>
<body>
<div class="ct">
<div class="main">
<input type="text" placeholder="http://a.com:8080/a.html">
</div>
<iframe src="http://b.com:8080/b.html" frameborder="0"></iframe>
</div>
</body>
<script>
document.querySelector('.main input').addEventListener('input', function() {
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "jrg.com";
var doma = document.domain;
console.log(doma)
</script>
-b.html:
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,body {
margin: 0;
}
input {
margin: 20px;
width: 200px;
}
</style>
</head>
<body>
<input id="input" type="text" placeholder="http://b.jrg.com:8080/b.html">
</body>
<script>
document.querySelector('#input').addEventListener('input',function() {
window.parent.document.querySelector('input').value = this.value;
})
document.domain = "jrg.com"
</script>
postMessage
- 利用window.postMessage通过监听messa进行交互以达到数据的传输。
- 如果你不希望从其他网站接收message,请不要为message事件添加任何事件侦听器。
- 如果你确实希望从其他网站接收message,请始终使用origin和source属性验证发件人的身份。 任何窗口(包括例如http://evil.example.com)都可以向任何其他窗口发送消息,并且你不能保证未知发件人不会发送恶意消息。 但是,验证身份后,你仍然应该始终验证接收到的消息的语法。 否则,你信任只发送受信任邮件的网站中的安全漏洞可能会在你的网站中打开跨网站脚本漏洞。
- 当你使用postMessage将数据发送到其他窗口时,始终指定精确的目标origin,而不是*。 恶意网站可以在您不知情的情况下更改窗口的位置,因此它可以拦截使用postMessage发送的数据。