参考链接:www.cnblogs.com/sdcs/p/8484…
一、 概述
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。其实我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。
# 什么是同源策略?
同源策略/SOP(Same origin policy)是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓同源是指"协议+域名+端口"三者相同,即便两个不同的域名指向同一个ip地址,也非同源。
同源策略限制以下几种行为:
1)Cookie、LocalStorage 和 IndexDB 无法读取
2)DOM 和 JavaScript 对象 无法获得
3)Ajax请求不能发送
既然有安全问题,那为什么又要跨域呢? 举个例子,假如公司内部有多个不同的子域,当需要从一个子域访问另一个子域的资源时就需要跨域。
二、JSONP
通常为了减轻web服务器的负载,我们把 js、css,img 等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建 script,再请求一个带参网址实现跨域通信。
1. 后端代码
const http = require("http");
const url = require("url");
http.createServer((req, res) => {
if(req.url === "/favicon.ico") return;
let {pathname, query} = url.parse(req.url, true);
switch(pathname) {
case "/info": {
// 定义返回数据
let _params = {name: "木子李", tel: "17398888669", job: "前端工程师"};
// 获取回调函数名
let _funcName = query.callback;
// 设置响应头
res.writeHead(200, { 'Content-Type': 'text/javascript' });
// 调用回调函数并将参数从后台传递给前台
res.end(`${_funcName}(${JSON.stringify(_params)})`);
}break;
}
}).listen(80, "0.0.0.0");
// 提示用户服务器相关信息
console.log("server running at http://127.0.0.1");
2. 原生实现
<!-- 1. 原生实现 -->
<script>
// 动态创建script标签
const script = document.createElement('script');
script.type = 'text/javascript';
// 传参并指定回调执行函数为onBack
script.src = `http://127.0.0.1/info?name=木子李&callback=onBack`;
document.body.appendChild(script);
// 回调执行函数
function onBack(res) {
console.log(res);
}
</script>
3. jquery ajax
$.ajax({
url: "http://127.0.0.1/info",
type: "GET",
dataType: "jsonp", // 请求方式为jsonp
jsonpCallback: "onBack", // 自定义回调函数名
data: {name: "木子李"}
});
4. vue.js
new Vue({
el: "#app",
mounted() {
this.$http.jsonp("http://127.0.0.1/info", {
params: {name: "木子李"},
jsonp: "callback"
}).then(res => {
console.log(res.body);
})
},
})
提示:需下载 vue-resource,并在vue之后引入。
jsonp缺点:只能实现 get 一种请求。
三、CROS
服务端设置 Access-Control-Allow-Origin 即可。
# nodeJS
res.setHeader('Access-Control-Allow-Origin', '*');
# express
app.all("*", (req, res, next) => {
//设置允许跨域的域名,*代表允许任意域名跨域
res.header("Access-Control-Allow-Origin","*");
//允许的header类型
res.header("Access-Control-Allow-Headers","content-type");
//跨域允许的请求方式
res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
if (req.method.toLowerCase() == 'options')
res.send(200); // 让options尝试请求快速结束
else
next();
});
# egg.js
① 安装依赖
$ npm install egg-cors
**② 在plugin.js中设置开启cors **
module.exports = {
cors: {
enable: true,
package: "egg-cors",
},
};
③ 在 config.{env}.js 中配置,注意配置覆盖的问题
// 安全性配置
config.security = {
// 支持post
csrf: {
enable: false,
ignoreJSON: true,
},
domainWhiteList: [],
};
// 处理跨域
config.cors = {
origin: "*",
allowMethods: "GET,HEAD,PUT,POST,DELETE,PATCH",
};
四、中间件代理跨域 DevServer
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/user',
target: 'http://127.0.0.1:80', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些https服务报错时用
cookieDomainRewrite: 'www.demo1.com' // 可以为false,表示不修改
}],
noInfo: true
}
}
五、WebSocket 协议跨域
WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器双向通信,同时允许跨域通讯,是server push技术的一种很好的实现。原生WebSocket API使用起来不太方便,我们使用Socket.io,它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。
1. 前端代码
<script src="./node_modules/socket.io-client/dist/socket.io.js"></script>
<script>
let socket = io('http://127.0.0.1:8080');
// 监听服务端消息
socket.on('/message', data => {
console.log('data from server: ---> ' + data);
});
setTimeout(() => {
socket.emit("/message", JSON.stringify({name: "木子李"}));
}, 3000);
</script>
2. 后端代码
const http = require("http");
const socket = require('socket.io')(http);
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end();
}).listen('8080', "0.0.0.0");
console.log('Server is running at port 8080...');
// 监听socket连接
socket.listen(server).on('connection', client => {
console.log('Client socket is open.');
// 接收信息
client.on('/message', function(data) {
// 发送消息
client.emit('/message', JSON.stringify({
name: "木子李",
tel: "17398888669",
job: "前端工程师"
}));
console.log('data from client: ---> ', data);
});
// 断开处理
client.on('disconnect', function() {
console.log('Client socket has closed.');
});
});
六、代理跨域 Nginx
参考:www.cnblogs.com/lovesong/p/…
七、postMessage
- 利用postMessage不能和服务端交换数据,只能在两个窗口(iframe)之间交换数据
- 两个窗口能通信的前提是,一个窗口以iframe的形式存在于另一个窗口,或者一个窗口是从另一个窗口通过window.open()或者超链接的形式打开的(同样可以用window.opener获取源窗口)