前端跨域方案简单总结
1、什么是跨域
【】跨域是一种浏览器同源安全策略,也即浏览器单方面限制脚本的跨域访问。很多人可能误认为资源跨域时无法请求,实质上请求是可以正常发起的(指通常情况下,部分浏览器存在部分特例),后端也可能正常进行了处理,只是在返回时被浏览器所拦截
【】跨域都是在讨论浏览器行为,包括各种webview容器,其中犹以 XmlHttpRequest
为主。正是由于javascript跑在浏览器之上,所以ajax
的跨域成了痛点
2、什么是同源策略
【】同源策略是一种约定,是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源
【】同源策略限制以下几种行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM和JS对象无法获得
- AJAX 请求不能发送
3、JSONP 跨域
【】jsonp的原理就是利用<script>
标签没有跨域限制,通过<script>
标签src
属性,发送带有callback
参数的GET
请求,服务端将接口返回数据拼凑到callback
函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback
函数返回的数据。
【】缺点是只能发送 get
一种请求
【】原生 js 实现
<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>
【】服务端返回如下(返回时即执行全局函数):
handleCallback({"success": true, "user": "admin"})
【】Vue axios 实现:
this.$http = axios;
this.$http.jsonp('http://www.domain2.com:8080/login', {params: {},jsonp: 'handleCallback'
}).then((res) => {console.log(res);
})
4、跨域资源共享(CORS)
【】允许浏览器向跨域服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 CORS需要浏览器和服务器同时支持
【】浏览器将CORS跨域请求分为简单请求和非简单请求,只要同时满足以下两个条件就是简单请求,否则为非简单请求
- 使用方法:post、get、head
- 请求的header是:Accept、Accept-Language、Content-Language、Content-Type
【】对于简单请求,浏览器直接发出CORS请求,在请求头信息中增加一个 Origin
字段,用来说明请求来自哪个域,服务器根据这个值决定是否同意这次请求
【】非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求。"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的
【】原生 ajax:
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);}
};
5、nginx代理跨域
【】NGINX 反向代理
6、nodejs中间件代理跨域
【】node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发
【】vue框架的跨域:node + vue + webpack + webpack-dev-server搭建的项目,跨域请求接口,直接修改webpack.config.js配置。开发环境下,vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域
【】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}
}
7、WebSocket协议跨域
【】WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,是server push技术的一种很好的实现。
【】前端代码
<div>user input:<input type="text"></div>
<script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io('http://www.domain2.com:8080');// 连接成功处理
socket.on('connect', function() {// 监听服务端消息socket.on('message', function(msg) {console.log('data from server: ---> ' + msg); });// 监听服务端关闭socket.on('disconnect', function() { console.log('Server socket has closed.'); });
});document.getElementsByTagName('input')[0].onblur = function() {socket.send(this.value);
};
</script>
【】Nodejs socket后台:
var http = require('http');
var socket = require('socket.io');// 启http服务
var server = http.createServer(function(req, res) {res.writeHead(200, {'Content-type': 'text/html'});res.end();
});server.listen('8080');
console.log('Server is running at port 8080...');// 监听socket连接
socket.listen(server).on('connection', function(client) {// 接收信息client.on('message', function(msg) {client.send('hello:' + msg);console.log('data from client: ---> ' + msg);});// 断开处理client.on('disconnect', function() {console.log('Client socket has closed.'); });
});
8、小结
【】jsonp(只支持get请求,支持老的IE浏览器)适合加载不同域名的js、css,img等静态资源;CORS(支持所有类型的HTTP请求,但浏览器IE10以下不支持)适合做ajax各种跨域请求;Nginx代理跨域和nodejs中间件跨域原理都相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,这适合前后端分离的前端项目调后端接口