- 什么是跨域
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对javascript施加的安全限制。
当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
- 广义的跨域:
1.) 资源跳转: A链接、重定向、表单提交
2.) 资源嵌入: <link>、<script>、<img>、
<frame>等dom标签,
还有样式中background:url()、
@font-face()等文件外链
3.) 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等
| 当前页面url | 被请求页面url | 是否跨域 | 原因 |
|---|---|---|---|
| www.test.com/ | www.test.com/index.html | 否 | 同源(协议、域名、端口号相同) |
| www.test.com/ | www.test.com/index.html | 跨域 | 协议不同(http/https) |
| www.test.com/ | www.baidu.com/ | 跨域 | 主域名不同(test/baidu) |
| www.test.com/ | blog.test.com/ | 跨域 | 子域名不同(www/blog) |
| www.test.com:8080/ | www.test.com:7001/ | 跨域 | 端口号不同(8080/7001) |
- 什么是同源策略
所谓同源(即指在同一个域)就是两个页面具有相同的协议,域名和端口号,即便两个不同的域名指向同一个ip地址,也非同源。
解决方案:
1、cors:服务器设置访问的白名单
2、jsonp:利用html的有些标签或者属性不受同源策略的限制,只能应用于get请求
3、代理服务器:服务器之间传递数据不受同源策略限制
同源策略限制以下几种行为:
1.) Cookie、LocalStorage 和 IndexDB 无法读取
2.) DOM 和 Js对象无法获得
3.) AJAX 请求不能发送
- 跨域解决方案
1、 通过jsonp跨域
2、 document.domain + iframe跨域
3、 location.hash + iframe
4、 window.name + iframe跨域
5、 postMessage跨域
6、 跨域资源共享(CORS)
7、 nginx代理跨域
8、 nodejs[中间件](https://cloud.tencent.com/product/tdmq?from=10680)代理跨域
9、 WebSocket协议跨域
1. 通过jsonp跨域
jsonp缺点:只能实现get一种请求。
(1)原生实现:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = 'http://www.domain2.com:8080/
login?user=admin&callback=onBack';
document.head.appendChild(script);
// 回调执行函数
function onBack(res) {
alert(JSON.stringify(res));
} </script>
服务端返回如下(返回时即执行全局函数):
onBack({"status": true, "user": "admin"})
(2)jquery ajax:
$.ajax({
url: 'http://www.domain2.com:8080/login',
type: 'get', dataType: 'jsonp',
// 请求方式为jsonp
jsonpCallback: "onBack",
// 自定义回调函数名
data: {}
});
(3)vue.js:
this.$http.jsonp('
http://www.domain2.com:8080/login',
{
params: {},
jsonp: 'onBack'}).then((res) =>
{ console.log(res);
})
后端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. 跨域资源共享(CORS)
CORS属于跨源 AJAX 请求的根本解决方法。
1、普通跨域请求:只需服务器端设置Access-Control-Allow-Origin
2、带cookie跨域请求:前后端都需要进行设置
【前端设置】根据xhr.withCredentials字段判断是否带有cookie
1、 前端设置:
(1)原生ajax
// 前端设置是否带
cookiexhr.withCredentials = true;
示例代码:
var xhr = new XMLHttpRequest();
// IE8/9需用
window.XDomainRequest兼容
// 前端设置是否带
cookiexhr.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)jQuery ajax
$.ajax({
...
xhrFields: {
withCredentials: true
// 前端设置是否带cookie
},
crossDomain: true,
// 会让请求头中包含跨域的额外信息,
但不会含cookie
...
});
(3)vue框架 在vue-resource封装的ajax组件中加入以下代码:
Vue.http.options.credentials = true
2.服务端设置:
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,说明没设成功。
(1.)Java后台:
/*
* 导入包:
import javax.servlet.http.HttpServletResponse;
* 接口参数中定义:
HttpServletResponse response
*/response.setHeader
("Access-Control-Allow-Origin",
"http://www.domain1.com");
// 若有端口需写全(协议+域名+端口)
response.setHeader
("Access-Control-Allow-Credentials", "true");
(2.)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',
// 允许访问的域(协议+域名+端口)
'Set-Cookie':
'l=a123456;Path=/;Domain=www.domain2.com;
HttpOnly'
// HttpOnly:脚本无法读取cookie
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen('8080');
console.log('Server is running at port 8080...');
3. Nodejs中间件代理跨域
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
1.非vue框架的跨域(2次跨域)
利用node + express + http-proxy-middleware搭建一个proxy服务器。
(1)前端代码示例:
var xhr = new XMLHttpRequest();
// 前端开关:
浏览器是否读写cookiexhr.withCredentials = true;
// 访问http-proxy-middleware代理服务器
xhr.open('get', '
http://www.domain1.com:3000/login?
user=admin', true);
xhr.send();
(2)中间件服务器:
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use('/', proxy({
// 代理跨域目标接口
target: 'http://www.domain2.com:8080',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin',
'http://www.domain1.com');
res.header('Access-Control-Allow-Credentials',
'true');
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com'
// 可以为false,表示不修改}));
app.listen(3000);console.log('
Proxy server is listen at port 3000...');
(3.)Nodejs后台同(六:nginx)
2、 vue框架的跨域(1次跨域)
利用node + webpack + webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了。
webpack.config.js部分配置:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080',
// 代理跨域目标接口
changeOrigin: true,
secure: false,
// 当代理某些https服务报错时用
cookieDomainRewrite: 'www.domain1.com'
// 可以为false,表示不修改
}],
noInfo: true
}
}