开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
前言
本篇文章总结了一些跨域的概念,解决跨域的一些方法。同掘友们一起分享本人的学习成果,如果有写的不对的地方,欢迎大家提出issues,相互学习,共同进步!
一. 跨域
当前页面中的某个接口请求的地址和当前页面的地址,如果协议、域名、端口其中有一项不同,就说该接口跨域了。
提示:跨域限制访问,其实是浏览器的限制
一般发起请求跨域会报如下错误:
1. 跨域的原因
浏览器为了保证网页的安全,出的同源协议策略。什么是同源策略呢? 百度词条给出如下解释:
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。
同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
举例: 当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面
当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,
即检查是否同源,只有和百度同源的脚本才会被执行。
如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
同源是指: 协议,域名,端口都相同,就是同源, 否则就是跨域
2.解决方式
1. CORS(通过设置后端允许跨域实现)
在请求头添加 Access-Control-Allow-Origin 属性,浏览器会判断响应中 Access-Control-Allow-Origin 值是否和当前的地址相同,匹配成功后才会做响应处理,否则继续报错。
缺点:会忽略cookie,而且对浏览器版本有一定的要求
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader("Access-Control-Allow-Methods", "GET, PUT, OPTIONS, POST");
2. node中间件、nginx反向代理
跨域限制的时候浏览器不能跨域访问服务器,node中间件和nginx反向代理,都是让请求发给代理服务器,静态页面面和代理服务器是同源的,然后代理服务器再向后端服务器发请求,服务器和服务器之间不存在同源限制。
2.1 node中间件
中间件就是一个函数。
在服务器开启之后和在路由响应之前,执行的一个函数,可以在这个中间件函数里边做我们想做的事。
并且这个函数里可以操作request,response。
var express = require('express')
var app = express()
// 中间件
app.use((req, res, next) => {
console.log('LOGGED1')
next()
})
app.use((req, res, next) => {
console.log('LOGGED2')
next() //执行下一个中间件
})
app.get('/', (req, res) => {
res.send('hello world')
})
app.listen(4399, () => {
console.log('服务器开启了');
})
2.2 Nginx反向代理
把web项目部署到和后端接口同源的当前本地的服务器上。Nginx配置就不在这边写了,一般由运维来做。前端主要做的是在vue.config.js中进行代理配置。
devServer: {
// 其他代码省略。。。。。
// 代理配置
proxy: {
// /api 是看接口文档,每当访问本地的/api接口时,会转化为访问真实的服务器
'/api': {
target: 'http://localhost:3000' // 我们要代理的真实接口地址
}
}
3. JSONP
通过动态创建script标签,通过script标签的src请求没有跨域限制来获取资源。缺点:只能发起get请求
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="/jsonp解决跨域.js"></script>
<script>
//事先准备好的函数fn和后端约定好
//backData就是后端通过参数传递过来的
function fn(backData) {
console.log('这是一个fnsb函数')
console.log(backData)
}
</script>
</head>
<body>
//把事先准备好的函数名,传递给要访问的后端接口
<script src="http:127.0.0.1:8080/all?callback=fn"></script>
</body>
</html>
4. postmessage
H5新增API,通过发送和接收API实现跨域通信。
otherWindow.postMessage(message, targetOrigin, [transfer]); otherWindow
- otherWindow: 其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行 window.open 返回的窗口对象、或者是命名过或数值索引的 window.frames。
对将接收消息的窗口的引用。获得此类引用的方法包括:
window.open:生成一个新窗口然后引用它;
window.opener: 引用产生这个的窗口;
HTMLIFrameElement.contentWindow:<iframe>从其父窗口引用嵌入式,可简单理解为从父级页面向自己页面进行数据传递;
window.parent:从嵌入式内部引用父窗口<iframe>,可简单理解为从子级页面向父级页面传递数据(当前场景下所用的window.parent);
window.frames+ 索引值(命名或数字);
-
message:向目标窗口发送的数据。它将会被结构化克隆算法序列化,所以无需自己序列化(部分低版本浏览器只支持字符串,所以发送的数据最好用JSON.stringify() 序列化)。
-
targetOrigin:通过 targetOrigin 属性来指定哪些窗口能接收到消息事件,可以是字面字符串 "*"(表示没有偏好),也可以是URI。但请始终提供一个具体的
targetOrigin,而不是*。如果不提供具体的目标,则会泄露你发送的信息。 -
transfer(可选):一系列可转移的对象,与消息一起被转移。这些对象的所有权被赋予目的地一方,它们不再能在发送方使用。
//发送方:
window.parent.postMessage('isClosed', 'http://yourhost.com');
//接收方:
window.addEventListener("message", msgHandler, false);
function msgHandler(event){
if (event.origin !== "http://yourhost.com") return;
// ...
}
实例参考文章: postMessage-跨域通信、 postMessage - 跨域消息传递
5. webSockets
它是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)
原理:在JS创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。 只有在支持web socket协议的服务器上才能正常工作。