背景
同源策略
同源策略是一种约定 是浏览器最核心也是最基本的安全功能
同源: “协议 域名 端口” 三者要保持相同
限制内容
本地的存储内容 如Cookie LocalStorage等等
三种标签允许跨越
<img src="">
<link href="">
<script src="">
解决方案
JSONP
基于script标签 不会受到影响 实现相当简单 兼容性好
缺点:
仅支持get方式请求
可能遭受XSS
function jsonp( url, params, callback ) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
/* 服务器通过callback来返回data */
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback } // wd=b&callback=show
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
jsonp({
url: 'http://localhost:3000/say',
params: { wd: 'Iloveyou' },
callback: 'show'
}).then(data => {
console.log(data)
})
/* 服务器 */
let express = require('express')
let app = express()
app.get('/say', function(req, res) {
let { wd, callback } = req.query
res.end(`${callback}('data')`)
})
app.listen(3000)
CORS
需要后端服务器实现CORS
简单请求
条件1:使用下列方法之一:
- GET
- HEAD
- POST
条件2:Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
服务器端设置Access-Control-Allow-Origin 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。浏览器请求时需要加入origin字段 服务器进行匹配决定是否同意此次请求
复杂请求
请求时会先进行 options 的请求(预检请求)
//在请求前进行拦截 设置响应报文头信息
app.all('*', (req, res, next) => {
res.header('Access-Control-Allow-Origin', '*')
next()
})
PostMessage
otherWindow.postMessage(message, targetOrigin, [transfer]);
message: 将要发送到其他 window的数据。
targetOrigin:通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin提供的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送。
transfer(可选):是一串和message 同时传递的 Transferable 对象. 这些对象的所有权将被转移给消息的接收方,而发送一方将不再保有所有权
/* a 向 b请求*/
// a.html
<iframe src="http://localhost:4000/b.html" frameborder="0" id="frame" onload="load()"></iframe> //等它加载完触发一个事件
//内嵌在http://localhost:3000/a.html
<script>
function load() {
let frame = document.getElementById('frame')
frame.contentWindow.postMessage('data', 'http://localhost:4000') //发送数据
window.onmessage = function(e) { //接受返回数据
console.log(e.data) //test
}
}
</script>
// b.html
window.onmessage = function(e) {
console.log(e.data) //test
e.source.postMessage('test', e.origin)
}
WebSocket(H5)
// socket.html
<script>
let socket = new WebSocket('ws://localhost:3000');
socket.onopen = function () {
socket.send('我爱你');//向服务器发送数据
}
socket.onmessage = function (e) {
console.log(e.data);//接收服务器返回的数据
}
</script>
// 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('我不爱你')
});
})
Node中间件代理
基于服务器之前请求无需遵循同源策略
中间件代理服务器需要和浏览器实现CORS
nginx反向代理
最简单的跨域方式 只需要修改nginx的配置
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录
window.name + iframe
通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
location.hash + iframe
实现原理: a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
document.domain + iframe
该方式只能用于二级域名相同的情况