Socket.io + Node.js跨域请求被阻止

160
我正在使用Node和Socket.io编写聊天应用程序。在Chrome上运行正常,但Mozilla会出现错误提示要启用跨域请求。

跨源请求被阻止:同源策略禁止读取远程资源http://waleedahmad.kd.io:3000/socket.io/?EIO=2&transport=polling&t=1401964309289-2&sid=1OyDavRDf4WErI-VAAAI。可以通过将资源移动到同一域或启用CORS来解决此问题。

这是启动Node服务器的代码。
var express = require('express'),
    app = express(), 
    server = require('http').createServer(app),
    io = require('socket.io').listen(server),
    path = require('path');
server.listen(3000);

app.get('/', function(req, res) {
    res.sendfile(__dirname + '/public/index.html');
});

在客户端。
var socket = io.connect('//waleedahmad.kd.io:3000/');

HTML页面上的脚本标签。

<script type="text/javascript" src="//waleedahmad.kd.io:3000/socket.io/socket.io.js"></script>

我也在应用程序的根目录中使用 .htaccess 文件。(waleedahmad.kd.io/node)。


(请注意,此处只是翻译,不包括任何解释)
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

5
你是否曾经让它成功运作?如果是,你的解决方案是什么? - elynnaie
31个回答

286

简单的服务器端修复

❗ 请勿使用“socketio”包,改用“socket.io”代替。 “socketio”已过时。某些用户似乎正在使用错误的包。

❗ 安全警告:设置来源*会使钓鱼网站能够模拟您网站的外观和感觉,然后让它像原样工作,同时窃取用户信息。如果您设置来源,则可以使他们的工作更加困难,而不是更容易。还应考虑使用CSRF token,这也是一个很好的想法。

socket.io v3

文档:https://socket.io/docs/v3/handling-cors/

CORS选项:https://www.npmjs.com/package/cors

const io = require('socket.io')(server, {
  cors: {
    origin: '*',
  }
});

socket.io < v3

const io = require('socket.io')(server, { origins: '*:*'});
或者
io.set('origins', '*:*');
或者
io.origins('*:*') // for latest version

* 单独使用无法起作用,这把我带进了兔子洞。


8
我正在使用最新版本,但是它不行,它给了我连接拒绝错误。 - gamer
6
从 v3.x 版本开始,请参考以下链接(https://socket.io/docs/v3/handling-cors/)和 (https://github.com/expressjs/cors#configuration-options)。var io = require('socket.io')(http, { 'cors': { 'methods': ['GET', 'PATCH', 'POST', 'PUT'], 'origin': true // 接受来自任何域的请求 } }); - vee
第一个可以运行,其他的都报错了。 - Zeyad Shaban
你可以使用 https://codesandbox.io/ 并设置 Node 沙盒... 邀请其他人进行编辑,我有时也会这样做,如果你这样做了,我可以看一下,否则就看不到你的代码了。 - King Friday
3
socket.io v4.4.1 只有第一个选项可用,添加 cors: { origin : '*',}} 解决了问题。 - Majd
显示剩余6条评论

62

我正在使用v2.1.0,但以上回答都无法解决我的问题,以下方法可以:

import express from "express";
import http from "http";

const app = express();
const server = http.createServer(app);

const sio = require("socket.io")(server, {
    handlePreflightRequest: (req, res) => {
        const headers = {
            "Access-Control-Allow-Headers": "Content-Type, Authorization",
            "Access-Control-Allow-Origin": req.headers.origin, //or the specific origin you want to give access to,
            "Access-Control-Allow-Credentials": true
        };
        res.writeHead(200, headers);
        res.end();
    }
});

sio.on("connection", () => {
    console.log("Connected!");
});

server.listen(3000);

1
我希望了解为什么在io.origin(":")上运行。这没有意义,我甚至尝试使用socket.io的origin处理程序来覆盖它,但除了这个之外什么都没用。我正在使用socket.io 1.7.4和socket.io-client 2.2.0以及socket.io-adapter-mongodb 0.0.2。 - The Bumpaster
1
我很感激找到了这篇文章,今天我花了5-6个小时来尝试在我的socket服务器上启用CORS。我真的尝试了stackoverflow上能找到的每一种方法。这是唯一对我有效的方法。我不明白为什么io.origin(“:”)在这种情况下也没有起作用。我以前构建过其他socket服务器,从未遇到过问题。如果有人有理论,我很想听听。无论如何,非常感谢分享! - octavemirbeau

36

您可以尝试在服务器端设置 origins 选项以允许跨域请求:

io.set('origins', 'http://yourdomain.com:80');

这里的http://yourdomain.com:80是您想要允许请求的源。

您可以在这里了解更多关于origins格式的信息。


28

对于想要了解新的Socket.io(3.x)的任何人,迁移文档相当有帮助。

特别是这段代码片段:

const io = require("socket.io")(httpServer, {
  cors: {
    origin: "https://example.com",
    methods: ["GET", "POST"],
    allowedHeaders: ["my-custom-header"],
    credentials: true
  }
});

太好了!现在我的套接字在进行POST请求时出现问题,我的vue socket.io客户端显示它是一个错误的握手。但我认为这个新错误是进步。非常感谢你! - frlzjosh
兄弟,我浪费了一周的时间尝试、搜索和研究文档。 但是最终解决了问题。我要和你分享几个秘诀:1)解决方案取决于socket.io的版本(检查你的package.json),在文档网站上选择相同的版本!!!! 2)注意文档中的异常情况。在我的情况下,"...cors: {origin: "*"}..."不起作用,因为连接需要凭据。所以,当你激活凭据时,你必须将origin声明为一个函数(在cors内部)origin: (_req, callback) => { callback(null, true); }, - m0rg4n

14

如果你遇到了 io.set not a function 或者 io.origins not a function 的问题,你可以尝试使用下面这种表示方法:

import express from 'express';
import { Server } from 'socket.io';
const app = express();
const server = app.listen(3000);
const io = new Server(server, { cors: { origin: '*' } });

11

我尝试了上面的方法,但是都没有起作用。下面这段代码来自socket.io文档,经过尝试有效。

io.origins((origin, callback) => {
  if (origin !== 'https://foo.example.com') {
      return callback('origin not allowed', false);
  }
  callback(null, true);
});

9

客户端:

const socket = io('https://sms-server.cedrick1227.repl.co/', { });

服务器:

const io = new socket.Server(server, { cors: { origin: '*' } });

对我来说,它的表现非常好。


2
你能多加一些解释吗?你的代码是做什么的,它如何帮助 OP? - Tyler2P
你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心找到有关如何编写良好答案的更多信息。 - Community
通常情况下,我在旧版中使用handlePreflightRequest配置值来解决这个问题,但在新版本(4.4.0)中,这个答案完全有效。谢谢。 - kodmanyagha

9
在我的情况下,我正在使用一个HTTP服务器和socket.io。
错误:
var http = require('http').Server(app);
var io = require('socket.io')(http);

解决方案:

var http = require('http').Server(app);
var io = require('socket.io')(http,  { cors: { origin: '*' } });

8

我想说的是,在尝试了很多方法后,解决我的CORS问题的方法就是使用一个旧版本的socket.io(2.2.0版)。我的package.json文件现在如下所示:

{
  "name": "current-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "devStart": "nodemon server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "socket.io": "^2.2.0"
  },
  "devDependencies": {
    "nodemon": "^1.19.0"
  }
}



如果你使用这个命令执行 npm install,你可能会发现在尝试使用 socket.io 时 CORS 问题不再存在。至少对我来说是有效的。

感谢兄弟,各种网站上的答案都没用,只有降级socket.io版本才行 :-( 它浪费了很多时间。非常感谢你。最终,使用 ^2.2.0 版本它可以运行了。 - VenkateshMogili

5
我也曾经遇到过这个问题,直到看到文档上说:"当origin为*时,你不能将withCredentials设置为true,你必须使用一个具体的origin:"。因此我的代码如下,希望有用:
WEB客户端
const { io } = require("socket.io-client");
const socket = io("localhost:3000", {
  extraHeaders: {
    "my-custom-header": "abcd"
  }
});

服务器

var express = require('express');
const app = express();
const http = require('http');
const httpServer = http.createServer(app);
const io = require("socket.io")(httpServer, {
  cors: {
    origin: '*',
    methods: ["GET", "POST"],
  }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接