何为跨域
想必大家都知道,不在赘述了,跨域问题出现的原因就是浏览器的安全机制:同协议、域名、端口。下面就总结下常用的几种解决方案。
1、JSONP(只能发送get请求,不支持post、put、delete;不安全xss攻击)
jsonp的详细介绍在另一篇文章,这里就以百度的查询接口做简单展示:
2、cors(后台配置)
下面以express为例
// 设置那些原可以访问接口
res.setHeader('Access-Control-Allow-Origin', origin)
// 允许携带哪个头访问
res.setHeader('Access-Control-Allow-Headers', 'name')
// 允许那些请求方法
res.setHeader('Access-Control-Allow-Methods', 'PUT,POST,DELETE')
// 允许携带cookie
res.setHeader('Access-Control-Allow-Credentials',true)
// 预检测存活时间(options请求)
res.setHeader('Access-Control-Max-Age',6000)
// 允许前端获取哪个请求头(允许返回的头)
res.setHeader('Access-Control-Expose-Header','name')
3、iframe postMessage
postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,且是为数不多可以跨域操作的 window 属性之一,它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的 iframe 消息传递
- 上面三个场景的跨域数据传递
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
otherWindow.postMessage(message, targetOrigin, [transfer]);
- message: 将要发送到其他 window 的数据。
- targetOrigin:通过窗口的 origin 属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个 URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配 targetOrigin 提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
- transfer(可选):是一串和 message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权。
接下来我们看个例子: http://localhost:3000/a.html页面向http://localhost:4000/b.html传递“我爱你”,然后后者传回"我不爱你"。
// a.html
function load() {
let frame = document.getElementById('frame')
frame.contentWindow.postMessage('我爱你', 'http://localhost:4000') //发送数据
window.onmessage = function(e) { //接受返回数据
console.log(e.data) //我不爱你
}
}
// b.html
window.onmessage = function(e) {
console.log(e.data) //我爱你
e.source.postMessage('我不爱你', e.origin)
}
4、window.name
window.name 属性的独特之处:name 值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)。
a、b页面同域,a页面嵌套c页面,onload事件第一次载入c页面url变为b页面并且获取contentWindow.name
// a.html(http://localhost:3000/b.html)
// c.html(http://localhost:4000/c.html)
5、hash
a中嵌套c,c中嵌套b,a=>b=>c传递location.hash,a页面用window.onhashchange获取hash值
// a.html
// b.html
// c.html
console.log(location.hash);
let iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/b.html#idontloveyou';
document.body.appendChild(iframe);
6、document.domain
必须是一级域名和二级域名的关系
// a.html
helloa
// b.html
hellob
7、websocket
可以参考阮大的文章。
// socket.html
// server.js
let express = require('express');
let app = express();
let WebSocket = require('ws');//记得安装ws
let wss = new WebSocket.Server({port:3000});
wss.on('connection',function(ws) {
ws.on('message', function (data) {
console.log(data);
ws.send('我不爱你')
});
})
8、ngxin
实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。
使用 nginx 反向代理实现跨域,是最简单的跨域方式。只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
先下载nginx,然后将 nginx 目录下的 nginx.conf 修改如下:
// proxy服务器
server {
listen 80;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
9、webpack proxy
//服务器启动目录;
devServer: {
contentBase: './dist',
hot: true,
// host:'1ocalhost',
port: 8586,
// compress:true,
//解决跨域
proxy: {
'/api': {
target: 'http://localhost:8087',
pathRewrite: { '^/api': '' },
changeOrigin: true,
secure: false, // 接受 运行在 https 上的服务
}
}
},