由于 CORS 错误,Socket.io 和 express 应用程序无法连接:“'Access-Control-Allow-Origin' 标头的值不能是通配符 '*'”
Posted
技术标签:
【中文标题】由于 CORS 错误,Socket.io 和 express 应用程序无法连接:“\'Access-Control-Allow-Origin\' 标头的值不能是通配符 \'*\'”【英文标题】:Socket.io and express app not connecting due to CORS error: “The value of the 'Access-Control-Allow-Origin' header must not be the wildcard '*'”由于 CORS 错误,Socket.io 和 express 应用程序无法连接:“'Access-Control-Allow-Origin' 标头的值不能是通配符 '*'” 【发布时间】:2020-07-17 17:00:30 【问题描述】:我正在慢慢失去理智,因为我遇到了一个非常愚蠢的问题。
我有一个 socket.io/express 应用程序作为 Docker 设置上传到 Digital Ocean。
为了允许 https,我使用 caddy 作为我的 Docker 设置的一部分以允许自动 https。
我一直在尝试通过我的域和位于 localhost:3000 上的本地 React 应用程序连接到此设置。但我不断收到以下错误:
从源 'http://localhost:3000' 访问 XMLHttpRequest 在 'https://mediaserver.domain.dev/socket.io/?EIO=3&transport=polling&t=N5BXNK2' 已被 CORS 策略阻止:响应中的 'Access-Control-Allow-Origin' 标头的值不能是通配符 '* ' 当请求的凭据模式为 'include' 时。 XMLHttpRequest 发起的请求的凭证模式由 withCredentials 属性控制。
我知道之前有很多关于此的 SO 问题,当我说我几乎尝试了所有这些问题时,相信我。
我尝试更改 cors 中间件的选项 我尝试添加自己的中间件并专门设置标头 我尝试使用 localhost:3000 作为来源 ...但似乎没有任何效果。我目前不知道我还能做些什么来解决这个问题。
所以欢迎任何帮助。
我的 docker-compose 文件如下所示:
version: '3.6'
services:
media-server:
build:
dockerfile: Dockerfile
context: ./
ports:
- "8080:5000"
expose:
- "5000"
caddy:
image: abiosoft/caddy:0.11.0
depends_on:
- "media-server"
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- /root/Caddyfile:/etc/Caddyfile
- /root/.caddy:/root/.caddy
我的 Caddyfile 如下:
https://mediaserver.domain.dev
proxy / http://media-server:8080
websocket
transparent
cors
我的服务器设置如下:
import cors from 'cors';
import express from 'express';
import socket from 'socket.io';
import intialiseWebSocketConnection from './socketio';
const app = express();
app.use(cors());
const server = app.listen(5000, function ()
console.log('Server is connectedd on *:5000');
);
const io = socket.listen(server);
intialiseWebSocketConnection(io);
【问题讨论】:
您是否尝试过 Access-Control-Allow-Origin: localhost:3000 开头的 http://?不只是 localhost:3000 @i.brod 我做了,对不起,应该更具体。我不确定是否应该在快速设置或球童中进行更改。 【参考方案1】:您正在尝试使用设置了凭据标志并将Access-Control-Allow-Origin
设置为任何 (*) 来发出跨域请求。出于安全原因,这是不允许的。有两种方法可以解决问题。如果您不需要发送凭据,请确保凭据标志为 false。也就是说,如果您使用的是XMLHttpRequest
,请确保withCredentials
不为真(默认为假)。如果您使用的是Fetch API
,请确保将Request.credentials
设置为"omit"
。
如果您出于某种原因确实需要发送凭据,则必须在服务器的响应中将 Access-Control-Allow-Origin
设置为您向服务器发送请求的来源,而不是任何 (*)。
要弄清楚您的来源是什么,只需检查您发送到服务器的请求中 Origin
标头设置的内容。
默认情况下,cors()
将 Access-Control-Allow-Origin
设置为 *。尝试将其更改为:
cors(
origin: "http://localhost:3000",
credentials: true
);
另请注意,Chrome 不支持将 localhost
作为来源。为了解决这个问题,您可以在开发过程中使用 --disable-web-security
标志启动 Chrome。
【讨论】:
感谢您的回复!在我的 React 应用程序中,我使用的是 socket.io 客户端,所以我不确定它在 XMLHttpRequest 的底层使用了什么,也不知道在哪里更改它。我认为我现在不需要凭据?我只是想让它暂时起作用。 @ReinVanImschoot 抱歉,没有仔细阅读您的问题。您无法更改 socket.io 中的凭据模式(我相信)。您将不得不更改 Access-Control-Allow-Origin 标头。重要的是 Chrome 不支持 localhost 作为来源,请注意这一点。我编辑了我的答案。希望有些东西有用。【参考方案2】:有一种简单的方法可以绕过 socket.io 连接的 CORS 错误。默认情况下,socket.io 使用几个常规的 http 调用来启动每个连接。如果一切顺利,它将转换为 webSocket 传输(并通过 webSocket 传输运行 socket.io)。这些初始 http 调用受 COR 限制。
但是,如果您告诉 socket.io 立即开始使用 webSocket 传输,那么您将不受 CORs 限制。您可以通过以下方式在客户端中执行此操作:
const socket = io(transports: ['websocket']);
尽管 webSocket 连接总是以一个 http 请求开始,但该特定 http 请求(带有适当的 upgrade
标头集)不受 COR 限制。
唯一的缺点(我知道)是您的代码无法在不支持 webSockets 的浏览器中运行。那将是一个非常非常旧的浏览器。甚至 IE10(2012 年发布)也支持 webSockets,所有现代浏览器至少从 2013 年开始就支持它们。我实际上不太确定为什么 socket.io 仍然将其 http 轮询作为默认设置,因为它在网络上的启动效率要低得多出每一个连接。无论如何,您可以使用io(transports: ['websocket']);
轻松绕过它。
【讨论】:
非常感谢!我也意识到了这一点。奇怪的是,当集成 websockets 时,他们仍然使用轮询。我可能只是从 socket.io 切换到更底层的 websocket 库。非常感谢您的回复! @ReinVanImschoot - Socket.io 有很多我认为值得的优点。请参阅此处并根据自己的需要决定:Moving from socket.io to raw webSockets. 现在我觉得这让一切变得更加困难,因为我还需要考虑这些网址“wss:/?mydomain.com/socket.io/?EIO=3&transport=websocket”,因此在 Caddy 等中设置规则。我对这一切都不是很有经验,所以我不确定 socket.io 现在是否是一个真正的帮助。由于我将它用作 WebRTC 的信令服务器,我可能会使用 Protoo 之类的东西?以上是关于由于 CORS 错误,Socket.io 和 express 应用程序无法连接:“'Access-Control-Allow-Origin' 标头的值不能是通配符 '*'”的主要内容,如果未能解决你的问题,请参考以下文章
在 Firefox 中运行 node.js 和 socket.io 时出现 CORS 错误
使用 socket.io / nginx / node 时出现 CORS 错误
即使我在服务器上允许它,Socket.io 也会出现 CORS 错误