前言

作为一名开发人员,相信无论是在项目中还是面试中,都会经常听到跨域这个词。大家或多或少对于跨域都有一定的了解。那么你知道几种跨域方式呢?欢迎在评论区留言。

什么是跨域?

浏览器出于安全考虑,只允许访问请求在相同域名相同端口相同协议下的脚本和接口。
协议、域名、端口有一个不满足的话 就会造成跨域。

浏览器实现了一种约定俗成的安全模式 -> 同源策略

URL 说明 是否允许通信
https://www.baidu.com/banner.js
https://www.baidu.com/search.js
相同域名下 允许通信
https://www.baidu.com:8080/detail.js
https://www.baidu.com/8000/apollo.js
相同域名 不同端口 不允许通信
https://www.baidu.com/test/date-range.js
https://www.baidu.com/utils/validate.js
相同域名 下不同文件夹中 允许通信
http://www.google.com/banner.js
https://www.google.com/search..js
相同域名 不同协议 不允许通信
https://bytedance.wenjuan.com/userInfo/question.html
https://live.wenjian.com/userInfo/question.html
主域相同 子域不同 不允许通信
http://12.35.46.87/login.js
http://blog.mintureChan/login.js
域名与域名对应的ip相同 不允许跨域
http://blog.mintureChan/login.js
http://mintureChan/login.js
域名相同 二级域名不同 不允许跨域
https://www.baidu.com/banner.js
https://www.google.com/banner.js
不同域名 不允许跨域

如上是各种不同方式的通信请求对比。

同源策略

同源策略,它是由Netscape提出的一个著名的安全策略

在浏览器中分别打开tab页面百度和谷歌

当浏览器的百度tab页执行一个脚本时 会检查这个脚本属于哪个页面。

即检查是否同源,只有和百度同源的脚本才会被执行。

如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。

同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发送请求的响应,即请求发送了,服务器响应了,但是无法被浏览器接收。

跨域解决方式

jsonp

jsonp是json的一种使用模式。HTML中<script>标签元素实现了这种使用模式。使用script标签可以获取到任意动态的JavaScript。

注意:script标签通过JavaScript直译器执行而不是用 JSON 解析器解析。

//使用jsonp获取fly.js
let fly = document.createElement('script')
fly.src = 'https://unpkg.com/flyio/dist/fly.min.js'
document.head.appendChild(fly)

使用场景:访问部署在不同域名下的包或访问第三方的sdk等。

jsonp的缺点: jsonp请求只支持get请求。 jsonp传输的文件可能会导致xss的攻击。

postMessage解决跨域

postMessage是web的一个Api window.postMessage()提供了一种受控机制来规避同源的这种安全模式。只要合理使用也可以很安全。

postMessage允许你从一个iframe向另一个iframe发送MessageEvent。所接收的page用window. addEventListener('message',( receiveMessage)=>{ //do some thing })来获取到发送页面的消息。

image.png

使用场景:在项目中对其他非同源部署的项目上进行通信传输。如: 项目中嵌套iframe地图发送骑手配送消息。

postMessage缺点:每次发送消息都需要进行监听 发送者和接收者都需要进行配置。并且在多次分发下显得代码重复臃肿。

CORS(跨域资源共享)

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端JavaScript代码获取跨域请求的响应。

同源策略默认阻止“跨域”获取资源。但是 CORS 给了web服务器权限,CORS允许跨域请求访问到不同源下的其他资源

当使用 XMLHttpRequest 发送请求时,浏览器会判断请求是否符合同源策略。如果不符合同源策略 浏览器会给该请求添加一个请求头-> Origin

跨域资源共享配置可以由后端进行配置也可以由前端进行配置

//ajax 实现CORS跨域
var xhr = new XMLHttpRequest()
var url = 'https://v1.apicloudMusic.com/banner.js'
//是否在请求中携带Cookie
xhr.withCredentials = true;

xhr.open('GET',url,false)
//预请求响应的回调
xhr.onreadystatechange = () =>{if(xhr.readyState === 4 && xhr.status ===200) //do some thing }
xhr.send()
//我们可以在java的请求拦截器中对部分需要跨域的接口进行处理

/*
* 导入包:import javax.servlet.http.HttpServletResponse;
* 接口参数中定义:HttpServletResponse response
*/

// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/' 第二份参数
response.setHeader("Access-Control-Allow-Origin", "https://v1.apicloudMusic.com");

// 允许前端携带cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials", "true");

//允许跨域访问的请求类型
response.setHeader("Access-Control-Allow-Method","POST, GET, PATCH, DELETE, PUT, OPTIONS");

值得注意的是 如果前端给请求头携带Cookie.后端一定需要配置Access-Control-Allow-Credentials

使用场景: 适用于不同服务之间的跨域请求。是目前主流的跨域解决方案之一

websocket实现跨域

websocketHTTP都是基于TCP协议的 因此使用websocket进行通信不需要考虑跨域的问题
但使用websocket进行通信需要保证websocket的唯一状态

//客户端
npm i socket.io --save

//假设页面存在id为myH3的dom
var domRender = document.getElementById('myH3')
let io = require("socket.io")
io.connect('http://localhost:8081');
io.on('data',(data)=>{
myH3.innerHTML = data
})

 //用node作为服务端向客户端发送信息
var httpServer = require('http').createServer();
var io = require("socket.io")(httpServer);
httpServer.listen(8080);
io.on("connection",function(client){
//客户端发送数据
client.emit("data","hello WebSocket from 8081");
});

这种通信方式也可以当作是一种跨域的解决方案

使用场景:适用于实时消息接收与发送。

nginx反向代理实现跨域

nginx作为一款轻量性的高性能web服务器 受到了无数开发人员的喜爱。它体积小。并发能力强,非常适合作为一个代理服务器进行分发。 使用nginx作为跨域解决方案也是目前开发者使用最多的一种方式
image.png
在本地起一个端口号为8070的node服务

image.png
假设在本地9998端口运行了如上图这个html文件 打开控制台会报出一个跨域的错误。我们使用nginx反向代理来解决这个问题。

下载nginx
nginx的配置一般由后端开发人员进行。一般使用的都是linux版本 为了方便 这里我就使用window版本的nginx进行配置
image.png

如上图 解压之后的nginx目录下会有这些文件夹。进入到 config文件夹中。找到一个名为nginx.conf的文件 这个文件就是nginx的基础配置文件。打开后进行修改:

image.png
监听localhost:9998端口 配置一个请求的代理。。将localhost:9998端口下所有经过/api/的请求路径全部代理到localhost:8070上 也就是说访问http://localhost:9998/api/getUserInfo的请求实际上变成了访问 http://localhost:8070/api/getUserInfo。该方式被称为反向代理。

再次启动9998端口访问html文件 控制台打印出了响应的信息。

image.png

node中间件代理跨域

使用node中间件进行代理跨域的原理和nginx代理跨域的原理基本一样 都是通过一个代理服务器的请求将数据进行转发实现同源操作。

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://localhost:9998/findList?query="BTC"', true);
xhr.send();

中间件服务器代码:

var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();

app.use('/', proxy({
// 代理跨域的地址
target: 'http://localhost:8070/api/getUserInfo',
changeOrigin: true,

// 修改CORS响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://localhost:9998');
res.header('Access-Control-Allow-Credentials', 'true');
},
}));

app.listen(9998);
console.log('Proxy server is listen at port 9998...');

webpack-dev-server

如果你使用过vue-cli + webpack搭建项目 那么webpack-dev-server 你一定不陌生。脚手架搭建项目 在本地启动的服务使用的就是webpack-dev-server 你可以在 package.json中的快捷执行命令行看到 npm run dev 后面执行的代码

在开发环境下 我们通常也会访问其他服务 比如文件服务,音视频服务。也会造成跨域的问题。在vue-cli项目中 我们可以修改vue.config.js文件中devServer这一项配置来解决开发环境中的跨域问题。

//vue.config.js 
const target = "http://minture-chan-glitch.com"
module.exports = {
entry: {},
module: {},
...
devServer: {
port: 8088,
open: true,
proxy: [
'/api': {
//target -> 代理目标的地址
target,
ws: true,
changeOrigin: true,
secure: false,
// pathRewrite 路径重写。
pathRewrite: {
'^/api': ''
},
headers: {
Referer: target
}
},
],
}
}

在本地端口8088中访问的所有包含/api的路径地址都会被代理到http://minture-chan-glitch.com 这种代理依靠的就是webpack-dev-server

最后

文本一共总结了七种跨域解决方案 希望能对你有所帮助。如果觉得文章有不妥之处 还请大家批评支持。

感谢您观看此篇文章 希望能给个👍评论收藏三连!你的点赞就是我写作的动力。