跨域解决方案
同源策略
跨域的应用
1.JSNONP
- 思路:通过script标签引入,在参数中拼上回调函数,响应中返回定义的函数执行
- jsonp帮我们创建一个函数,并且创建立一个script
- script,img,iframe不受同源策略的影响
- 缺点: 只能发送get请求并且不安全,可能引起xss攻击
function Jsonp({ url, params, cb }) {
return new Promise(function(reslove, reject) {
let script = document.createElement('script')
let paramsArr = []
window[cb] = function(data) {
reslove(data)
}
let params = { ...params, cb }
for(let key in params) {
paramsArr.push(`${key}=${params[key]}`)
}
script.src = `${url}?${[paramsArr.join('&')]}`
document.body.appendChild(script)
document.removeChild(script)
})
}
2.cors
- 靠服务端验证,因为安全性高,现在是主流
- origin: 当前访问的是哪个源
服务器验证客户端传来的origin,在白名单内,服务器响应头设置'Access-Control-Allow-Origin'
Access-Control-Allow-Origin: 可以访问的源或者'*'
Access-Control-Allow-Headers: 允许携带哪个头,'a, b',或者'*'
Access-Control-Allow-Methods: 'PUT, ....'或者'*'
Access-Control-Max-Age: 6000 // 预检的存活事件
cookie
- 设置cookie: document.cookie = 'name=xxx',cookie默认不用许跨域
- xhr.withCredentials = true
- 服务端设置Access-Control-Allow-Credentials:true // 允许携带cookie
3.iframe + postmessage
// A.html
<iframe src="./B.html" fzrazmeborder="0" id="iframe" onload="load()"></iframe>
<script>
function load() {
let iframe = document.getElementById('iframe')
iframe.contentWindow.postMessage('xxx', './B.html') // ifame内部的window就是B
}
window.onmessage = function(e) {
// e.data // B发过来的信息
}
</script>
// B.html
window.onmessage = function (e) {
// e.data // A发过来的信息
e.source.postMessage(xxx, './A.html')
}
4. iframe + document.domain
// A.html
<iframe src="./B.html" frameborder="0" onload=""load></iframe>
<script>
document.domain = 'xxx'
function load() {
let iframe = document.getElementById('iframe')
console.log(iframe.contentWindow.a) // 1
}
</script>
// B.html
document.domain = 'xxx'
let a = 1
5. iframe + hash
- 路径后的hash值可以用来通信
- A和B是同源, C是独立域的
- A给C传一个hash。C收到hash值后,把hash值传给B, B将结果放在A的hash中
// A.html,给C设置一个hash
<iframe src="./C.html#xxx" frameborder="0"></iframe>
<script>
window.addEventListener('hashChange', function() {
console.log(location.hash)
})
</script>
// B.html, 和A.html同源,在B中改变A的hash
window.parent.parent.location.hash = location.hash
// C.html,将hash传给B
<script>
let iframe = document.createElement('iframe')
iframe.src = './B.html#xxx'
document.body.appendChild(iframe)
</script>
6. iframe + window.name
- A和B是同源, C是独立域的
- A引用C,C把值放在window的name上,把A引用地址改像B
// A.html
<iframe src="./C.html" frameborder="0" id="iframe" onload="load()"></iframe>
<script>
let first = true
function load() {
let iframe = document.getElementById('iframe')
iframe.src = './B.html'
first = false
} else {
console.log(iframe.contentWindow.name) // xxx
}
}
</script>
// B.html和A.html同源
// C.html
<script>
window.name = 'xxx'
</script>
7.socket-io
- 双工通信,A发给服务端
- 基于WebSocket,有个socket-io库
- socket-io没有同源限制
A.html
<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>
// node.js后台
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就是客户端,接收信息
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.nginx
server {
listen 81;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080;
proxy_cookie_domain www.domain2.com www.domain1.com;
index index.html index.htm;
add_header Access-Control-Allow-Origin http://www.domain1.com;
add_header Access-Control-Allow-Credentials true;
}
}
9.http-proxy(反向代理)
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
}
}