# 03.什么是跨域?
# 1.同源策略如下:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js | 同一域名下 | 允许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js | 同一域名下不同文件夹 | 允许 |
http://www.a.com:8000/a.js http://www.a.com/b.js | 同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js | 主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js | 同一域名,不同二级域名(同上) | 不允许(cookie这种情况下也不允许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js | 不同域名 | 不允许 |
特别注意两点:
第一:如果是协议和端口造成的跨域问题“前台”是无能为力的;
第二:在跨域问题上,域仅仅是通过“URL
的首部”来识别而不会去尝试判断相同的ip
地址对应着两个域或两个域是否在同一个ip
上。
同源策略限制以下几种行为:
1.) Cookie、LocalStorage 和 IndexedDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送
2
3
# 2.跨域解决方案
1、 通过jsonp
跨域
2、 document.domain + iframe
跨域
3、location.hash + iframe
4、window.name + iframe
跨域
5、postMessage
跨域
6、 跨域资源共享(CORS
)
7、 nginx
代理跨域
8、 nodejs
中间件代理跨域
9、 WebSocket
协议跨域
# 一、 通过jsonp跨域
通常为了减轻web服务器的负载,我们把js
、css
,img
等静态资源分离到另一台独立域名的服务器上,在html
页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script
,再请求一个带参网址实现跨域通信。
1.)原生实现:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringify(res));
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
服务端返回如下(返回时即执行全局函数):
handleCallback({"status": true, "user": "admin"})
2.)vue.js:
this.$http.jsonp('http://www.domain2.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res) => {
console.log(res);
})
2
3
4
5
6
后端node.js代码示例:
var querystring = require('querystring');
var http = require('http');
var server = http.createServer();
server.on('request', function(req, res) {
var params = qs.parse(req.url.split('?')[1]);
var fn = params.callback;
// jsonp返回设置
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.write(fn + '(' + JSON.stringify(params) + ')');
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jsonp缺点:只能实现get
一种请求。
# 二、 跨域资源共享(CORS)
普通跨域请求:
只服务端设置Access-Control-Allow-Origin
即可,前端无须设置,若要带cookie
请求:前后端都需要设置。
需注意的是:
由于同源策略的限制,所读取的cookie
为跨域请求接口所在域的cookie
,而非当前页。如果想实现当前页cookie
的写入,可参考下文:七、nginx反向代理中设置proxy_cookie_domain 和 八、NodeJs中间件代理中cookieDomainRewrite参数的设置。
目前,所有浏览器都支持该功能(IE8+
:IE8/9
需要使用XDomainRequest
对象来支持CORS
)),CORS
也已经成为主流的跨域解决方案。
# 1、 前端设置:
1.)原生ajax
// 前端设置是否带cookie
xhr.withCredentials = true;
2
示例代码:
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.com:8080/login', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
2.)vue框架
a.) axios设置:
axios.defaults.withCredentials = true
b.) vue-resource设置:
Vue.http.options.credentials = true
# 2、 服务端设置:
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。
1.)Nodejs后台示例:
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var postData = '';
// 数据块接收中
req.addListener('data', function(chunk) {
postData += chunk;
});
// 数据接收完毕
req.addListener('end', function() {
postData = qs.parse(postData);
// 跨域后台设置
res.writeHead(200, {
'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie
'Access-Control-Allow-Origin': 'http://www.domain1.com', // 允许访问的域(协议+域名+端口)
/*
* 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
* 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
*/
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly的作用是让js无法读取cookie
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen('8080');
console.log('Server is running at port 8080...');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34