跳到主要内容

浏览器跨域及解决方案

长念
长念阅读约 6 分钟3 年前发布2 年前编辑

关于跨域问题

同源策略

同源策略(Same-origin policy)是 浏览器 中一个重要的安全策略,它用于限制不同源之间的资源交互。其目的是为了帮助阻隔恶意文档,减少可能被攻击的媒介。

提示

同源三要素:协议 + 域名 + 端口

示例:https://www.baidu.com/index.html

网址是否同源描述
https://www.baidu.com/about.html只有路径不同
http://www.baidu.com/index.html协议不同 http
https://www.baibu.com/index.html很明显域名 (主机) 不同
https://www.baidu.com:8000/index.html端口号不同 http 默认是 80 端口

因为浏览器同源策略的限制,不同源之间资源加载存在跨域问题,跨域问题只存在于浏览器环境中

注意
  • APP、小程序中不存在跨域问题。
  • HTML 标签中许多 src 属性是可以跨域请求的,比如 <img> <script>

跨域发生在什么时候

在浏览器接收到服务器响应之后。 默认情况下,浏览器发送的跨域请求,实际上服务器接收并响应了,只不过响应结果被浏览器拦截了,因为同源策略。细分的话有两种情况:

  • 简单请求:服务器处理并相应;
  • 复杂请求:浏览器会发送预检请求,服务器只是应答了预检请求,预检请求的响应结果被浏览器拦截,所以并未收到真正的业务请求,也就不会处理真实业务逻辑。

跨域资源共享

跨域资源共享 (CORS, Cross Origin Resource Sharing) 是一种基于 HTTP 头的机制,该机制可以通过服务器指定能够访问它的源,这使浏览器允许这些源访问加载自己的资源。

当浏览器认为即将要执行的请求可能会对服务器造成不可预知的影响时,会在正式请求资源之前,先发送一个 preflight 预检请求 (preflight 请求类型为 OPTIONS),根据服务器响应判断是否允许即将执行的请求。

响应头中通常包含这些信息:

Access-Control-Allow-Headers: Content-Type, Content-Length, Authorization, Accept, X-Requested-With
Access-Control-Allow-Origin: http://www.baidu.com
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400

其中 Access-Control-Allow-Origin 就表示当前服务器允许的源,如果与当前源不匹配或不存在该字段,就会触发跨域错误。

解决方案

前端代理

应用场景: 本地开发

前端代理是通过 node 中间件实现代理来转发我们的请求,由于 node 本身不受浏览器的限制,所以可以和服务器正常通讯。简单示意图如下:

代理示意图

其中 Proxy Server 在客户端。

注意

proxy 只是解决本地开发的跨域访问问题。如果在生产环境上发生跨域问题的话,可以将类似的配置转移到 Nginx 容器上。

webpack-dev-server 配置

前端基于 webpack 进行打包的项目都会以 webpack-dev-server 来运行,前端可以在开发环境设置代理解决跨域问题:

webpack.config.js
devServer: {
proxy: {
'/api': {
target: 'http://target.origin',
changeOrigin: true, //是否跨域
pathRewrite: { '^/api': '' }
}
}
}

需要注意的是,代理只是将目标服务器返回的数据传递到前端。所以你在浏览器上看到的请求地址还是 http://localhost:8000/api/xxx。 即浏览器访问代理服务器,代理将请求发往服务器,并将收到的结果传递给浏览器。

vite 配置

vitewebpack 的写法略有不同,更多写法可参考 Vite 官方中文文档

vite.config.js
server: {
proxy: {
'/api': {
target: 'http://target.origin',
changeOrigin: true, //是否跨域
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}

后端

应用场景: 应用比较少吧,因为开发环境和生产环境部署时的源不相同,而且每次在不同的服务器部署时都需要改代码去调整允许访问的源也不现实。一般也不建议设为 *

设置响应头中允许的源

通过配置允许的源,在响应头中将请求源添加到 Access-Control-Allow-Origin 中,浏览器同源策略就能判定为同源请求。不同框架的 API 可能不同,但方法都是一样的,下面是 express 的示例:

// 判断当前源是否在允许列表内
const isAllowedOrigin = (origin) => {
// 同时设定多个允许的源
const allowedOrigins = ['http://localhost:3300', 'http://127.0.0.1:3300'];
return allowedOrigins.includes(origin);
};

app.all('*', function (req, res, next) {
const { origin } = req.headers;
if (!isAllowedOrigin(origin)) {
return;
}
res.header('Access-Control-Allow-Origin', origin);
next();
});

服务端代理

应用场景: 线上部署

Nginx 反向代理

前端分离项目部署通常也都需要解决跨域问题,因为前后端项目部署在不同的端口,甚至不同的服务器上。通过 Nginx 转发请求,不会触发同源策略。

{
server {
listen 3000;
server_name localhost;

# 前端部署配置
location / {
root /var/www/app; # 配置应用的文件夹
index index.html index.htm;
try_files $uri $uri/ /index.html;
}

# 代理后端 API 的配置
location /api/ { # 用于转发的后端地址标志
proxy_pass http://192.168.0.1:8080/; # 后端部署的真实地址
}
}