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

我正在使用 node 和 socket.io 编写一个聊天应用程序。它在 Chrome 上运行良好,但是 Mozilla 在启用跨源请求时出现了一个错误。

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

这是启动节点服务器的代码。

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>

我还在 app root 目录中使用. 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"
252740 次浏览

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

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

这里的 http://yourdomain.com:80是您希望允许从中发出请求的原点。

你可以阅读更多关于 origins格式 给你的内容

这可能是 Firefox 的认证问题,不一定是 CORS 的错误

当 Socketio 和 Nodejs 在 Firefox 中抛出 CORS 错误时,我遇到了同样的问题。我使用 Certs for * . myNodeSite.com,但我引用的是 Nodejs 的 LAN IP 地址192.168.1.10。(WAN IP 地址也可能抛出同样的错误。)由于 Cert 与 IP 地址引用不匹配,Firefox 抛出了这个错误。

简单的服务器端修复

Something 不要使用“ socketio”包... 而是使用“ socket.io”。“ Socketio”已经过时了。一些用户似乎使用了错误的软件包。

Something 安全警告: 设置起源 *打开钓鱼网站的能力,模仿你的网站的外观和感觉,然后有它的工作原理是一样的,而欺诈用户信息。如果你设置了原点,你可以使他们的工作更难,而不是更容易。另外,考虑使用 CSRF 令牌也是一个很好的主意。

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

单靠 *无法工作,这让我陷入了困境。

在 StakOverflow 和其他论坛上阅读了大量的主题后,我找到了一个可行的解决方案。这个解决方案是用于工作 没有快递的。

这是先决条件。


服务器端

// DEPENDENCIES
var fs       = require('fs'),
winston  = require('winston'),
path     = require('path');




// LOGS
const logger = winston.createLogger({
level     : 'info',
format    : winston.format.json(),
transports: [
new winston.transports.Console({ level: 'debug' }),
new winston.transports.File({ filename: 'err.log', level: 'err' }),
new winston.transports.File({ filename: 'combined.log' })
]
});




// CONSTANTS
const Port          = 9000,
certsPath     = '/etc/letsencrypt/live/my.domain.com/';




// STARTING HTTPS SERVER
var server = require('https').createServer({
key:                fs.readFileSync(certsPath + 'privkey.pem'),
cert:               fs.readFileSync(certsPath + 'cert.pem'),
ca:                 fs.readFileSync(certsPath + 'chain.pem'),
requestCert:        false,
rejectUnauthorized: false
},
(req, res) => {


var filePath = '.' + req.url;
logger.info('FILE ASKED : ' + filePath);


// Default page for visitor calling directly URL
if (filePath == './')
filePath = './index.html';


var extname = path.extname(filePath);
var contentType = 'text/html';


switch (extname) {
case '.js':
contentType = 'text/javascript';
break;
case '.css':
contentType = 'text/css';
break;
case '.json':
contentType = 'application/json';
break;
case '.png':
contentType = 'image/png';
break;
case '.jpg':
contentType = 'image/jpg';
break;
case '.wav':
contentType = 'audio/wav';
break;
}


var headers = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'OPTIONS, POST, GET',
'Access-Control-Max-Age': 2592000, // 30 days
'Content-Type': contentType
};


fs.readFile(filePath, function(err, content) {
if (err) {
if(err.code == 'ENOENT'){
fs.readFile('./errpages/404.html', function(err, content) {
res.writeHead(404, headers);
res.end(content, 'utf-8');
});
}
else {
fs.readFile('./errpages/500.html', function(err, content) {
res.writeHead(500, headers);
res.end(content, 'utf-8');
});
}
}
else {
res.writeHead(200, headers);
res.end(content, 'utf-8');
}
});


if (req.method === 'OPTIONS') {
res.writeHead(204, headers);
res.end();
}


}).listen(port);




//OPENING SOCKET
var io = require('socket.io')(server).on('connection', function(s) {


logger.info("SERVER > Socket opened from client");


//... your code here


});

客户端

<script src="https://my.domain.com:port/js/socket.io.js"></script>
<script>
$(document).ready(function() {


$.socket = io.connect('https://my.domain.com:port', {
secure: true // for SSL
});


//... your code here


});
</script>

我尝试了上面的方法,但是没有一个对我有用。下面的代码来自于 Socket.io 文档,它起作用了。

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

好吧,我有一些问题得到这个工作使用自签名的证书进行测试,所以我要复制我的设置,为我工作。如果您没有使用自签名的证书,您可能不会有这些问题,希望!

根据你的浏览器 Firefox 或 Chrome 的不同,你可能会遇到不同的问题,我将在一分钟内解释。

首先安装:

客户

// May need to load the client script from a Absolute Path
<script src="https://www.YOURDOMAIN.com/node/node_modules/socket.io-client/dist/socket.io.js"></script>
<script>
var options = {
rememberUpgrade:true,
transports: ['websocket'],
secure:true,
rejectUnauthorized: false
}
var socket = io.connect('https://www.YOURDOMAIN.com:PORT', options);


// Rest of your code here
</script>

服务器

var fs = require('fs');


var options = {
key: fs.readFileSync('/path/to/your/file.pem'),
cert: fs.readFileSync('/path/to/your/file.crt'),


};
var origins = 'https://www.YOURDOMAIN.com:*';
var app = require('https').createServer(options,function(req,res){


// Set CORS headers
res.setHeader('Access-Control-Allow-Origin', 'https://www.YOURDOMAIN.com:*');
res.setHeader('Access-Control-Request-Method', '*');
res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET');
res.setHeader('Access-Control-Allow-Headers', '*');
if ( req.method === 'OPTIONS' || req.method === 'GET' ) {
res.writeHead(200);
res.end();
return;
}


});


var io = require('socket.io')(app);


app.listen(PORT);

对于开发,客户端使用的选项在生产中是可以的,你会想要这个选项:

 rejectUnauthorized: false

你很可能希望设置为“ true”

接下来的事情是,如果它是一个自签名的证书,您将需要访问您的服务器在一个单独的页面/选项卡,并接受证书或导入到您的浏览器。

对于 Firefox,我一直得到这个错误

MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT

我的解决方案是添加以下选项并在不同的页面/选项卡中接受证书。

{
rejectUnauthorized: false
}

在 Chrome 中,我不得不打开另一个页面并接受认证,但在那之后,一切都运行良好,无需添加任何选项。

希望这个能帮上忙。

参考文献:

Https://github.com/theturtle32/websocket-node/issues/259

Https://github.com/socketio/engine.io-client#methods

我使用的是 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);

我在使用 socket.io 和 node.js & React 开发聊天应用程序时遇到了问题。同样,这个问题对于 Firefox 浏览器来说并不太重要,我在 Edge & Chrome 中也面临同样的问题。

“跨源请求被阻止,并被其他一些资源使用... ...”

然后,我在 project 目录下载 cors,并将其放入服务器文件 index.js 中,如下所示: 要下载,只需使用 node.js 键入 command:

Npm 安装系统

const cors = require('cors');
app.use(cors());

这将允许 CORS 被文件中的不同资源使用,并允许浏览器中的跨源请求。

我也有同样的问题,任何解决办法都对我有效。

原因是我使用 allowRequest 来接受或拒绝使用我在查询参数中传递的令牌的连接。

我在客户端的查询参数名中有一个输入错误,所以连接总是被拒绝,但是浏览器抱怨 cors..。

一旦我修复了输入错误,它就像预期的那样开始工作了,而且我不需要使用任何额外的东西,全局快速 cors 设置就足够了。

因此,如果您正在使用 allowRequest,请检查该函数是否正常工作,因为它抛出的错误在浏览器中显示为 cors 错误。我猜,除非您想要拒绝连接时手动添加 cors 头。

如果你得到的是 io.set not a functionio.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: '*' } });

对于任何在这里寻找新的 Socket.io (3.x)的人来说,移民文件是相当有帮助的。

特别是下面这段:

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

我只是想说,在尝试了一系列事情之后,解决 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 和 socket.io-client 使用相同的版本解决了我的问题。

看看这个: 完整的例子

服务器:

let exp = require('express');
let app = exp();


//UPDATE: this is seems to be deprecated
//let io = require('socket.io').listen(app.listen(9009));
//New Syntax:
const io = require('socket.io')(app.listen(9009));


app.all('/', function (request, response, next) {
response.header("Access-Control-Allow-Origin", "*");
response.header("Access-Control-Allow-Headers", "X-Requested-With");
next();
});

客户:

<!--LOAD THIS SCRIPT FROM SOMEWHERE-->
<script src="http://127.0.0.1:9009/socket.io/socket.io.js"></script>
<script>
var socket = io("127.0.0.1:9009/", {
"force new connection": true,
"reconnectionAttempts": "Infinity",
"timeout": 10001,
"transports": ["websocket"]
}
);
</script>

许多天前,我从堆栈溢出答案的组合中记得这个问题,但是我找不到提到它们的主要链接

有时节点服务器停止时会遇到此问题。 因此,请检查您的节点服务器是否正常工作。

那你可以用 Io.set (“起源”、“ http://yourdomain.com:port_number”) ;

我在 easyRTC 中使用了2.4.0版的 socket.io,并在 server _ ssl. js 中使用了以下代码,这些代码对我很有用

io = require("socket.io")(webServer, {
handlePreflightRequest: (req, res) => {
res.writeHead(200, {
"Access-Control-Allow-Origin": req.headers.origin,
"Access-Control-Allow-Methods": "GET,POST,OPTIONS",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Referer, User-Agent, Host, Authorization",
"Access-Control-Allow-Credentials": true,
"Access-Control-Max-Age":86400
});
res.end();
}
});

如果你的 socket.io 应用可以在 Chrome,Safari 和其他浏览器上运行,但是你仍然在 Firefox 中遇到 CORS 问题,而且你使用的是自签名证书,那么问题就是 Firefox 默认不接受自签名证书,你必须通过 Firefox 的首选项 > 证书 > 查看证书 > 添加异常来添加异常。

如果你不这样做,那么 Firefox 会显示你发布的错误,这是误导性的,但是在它的开发者工具中,你会发现这个错误: MOZILLA _ PKIX _ ERROR _ SELF _ SIGNED _ CERT。这表明 Firefox 根本不接受证书,因为它是自签名的。

以下是来自官方文件的解决方案:

从 Socket.IO v3开始,您需要显式地启用跨来源资源共享(CORS)。

const io = require("socket.io")(httpServer, {cors: {
origin: "https://example.com", // or "*"
methods: ["GET", "POST"]}});
const options = {
cors: {
origin:
String(process.env.ORIGINS_STRING) === "ALL"
? true
: String(process.env.ORIGINS_STRING).split(","),
methods: ["GET", "PUT", "POST", "DELETE"],
allowedHeaders: [
"Access-Control-Allow-Headers",
"X-Requested-With",
"X-Access-Token",
"Content-Type",
"Host",
"Accept",
"Connection",
"Cache-Control",
],
credentials: true,
optionsSuccessStatus: 200,
},
};

在. env 文件中:

ORIGINS_STRING=ALL

或者

ORIGINS_STRING=http://localhost:8080,http://localhost:8081

对于后端,我只是将 socket.io 的版本从2.x.x 更新到4.1.2,并执行相同的 ie。将前端的 socket.io-client 版本从2.x.x 升级到了4.1.2... ... 并且它工作了

我和 socket.io: 4.2.xnode: 14.17.x@hapi/hapi: 20.1.x一起工作。

在尝试了其他答案中提到的多种方法后,我发现这些版本唯一可行的解决方案是:

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

请确保在 options 对象中有 { cors: { origin: '*' } }

因此,基本上在 v2中,Socket.IO 服务器会自动添加必要的报头,以允许跨来源资源共享(CORS) ,因此客户端和服务器之间的连接没有问题。但是这种行为虽然方便,但是在安全性方面并不是很好,因为它意味着所有域都可以到达您的 Socket.IO 服务器。

在 v3及以上版本中,CORS 在默认情况下是禁用的。因此,您需要在服务器端脚本上显式地启用它们。

我的代码示例:

在 socket.io 的 v2中,服务器脚本如下:

const io = require('socket.io')(8000);

但是在 v3及以上版本中,这些代码变成:

const io = require('socket.io')(8000, {
cors: {
origin: ['http://localhost:5500'],
},
});

//通过设置 cors,记住允许客户机与套接字服务器通信

//在这种情况下,8000是运行套接字连接的端口,5500是托管客户端文件的端口。

//Socket 连接在不同的端口上运行,客户端文件在不同的端口上运行

//还需要在安装了 socket.io 模块的地方安装 socket.io-client

为了进一步澄清,我添加了我的文件

这是我的 HTML 文件:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="http://localhost:8000/socket.io/socket.io.js"" content="text/javascript"></script>
<script src="javascript/client.js"></script>
<link rel="stylesheet" href="css/style.css">
<title>Chat App</title>
</head>
<body>
</body>
</html>

这是我的 javascript/client.js

const socket = io('http://localhost:8000/');

这是 server/server.js

const io = require('socket.io')(8000, {
cors: {
origin: ['http://localhost:5500'],
},
});


io.on('connection', socket =>{
console.log(socket.id);
});

//如果你仍然不能得到它更详细的信息可以在 https://socket.io/docs/v4/migrating-from-2-x-to-3-0/#CORS-handling上看到

//还有一个视频,我从中得到了这个解决方案 https://youtu.be/ZKEqqIO7n-k

客户:

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

服务器:

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

对我来说就像魔法一样。

在我的例子中,我使用 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: '*' } });

我在隐式的 http 服务器上使用 SocketIO,我使用的是4.4版的 SocketIO,我必须在服务器上这样做:

const { Server } = require("socket.io");


const io = new Server(PORT, {})
io.engine.on("headers", (headers, req) => {
headers["Access-Control-Allow-Origin"] = "http://yourdomain.com"
headers["Access-Control-Allow-Headers"] = "origin, x-requested-with, content-type"
headers["Access-Control-Allow-Methodsn"] = "PUT, GET, POST, DELETE, OPTIONS"
})

对于那些使用 socket.io > = v4.4.0的用户

因为我需要 CORS 的选择只是为了当地的发展,没有工作在这里为我。

我实现的解决方案,后端:

    const io = require("socket.io")(server, {
path: '/api/socket.io',
});


if (process.env.NODE_ENV === 'development') {
io.engine.on('initial_headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = 'http://localhost:3000';
headers['Access-Control-Allow-Credentials'] = true;
});


io.engine.on('headers', (headers, req) => {
headers['Access-Control-Allow-Origin'] = 'http://localhost:3000';
headers['Access-Control-Allow-Credentials'] = true;
});
}

对我有效的组合是:

socketio = require('socket.io')(http, {
origins: process.env.WEB_URL, // http(s)://...
cors: {
origin: process.env.WEB_URL,
credentials: true
}
}).listen(process.env.SOCKET_PORT) // 8899


app.set('socketio', socketio)

只要在你的插座上这样做,要求:-

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

我也一直在纠结这个问题,直到我看到文档说: “你不能将‘ withCredenals’设置为 true 和 source: * ,你需要使用一个特定的 source:”。所以我的代码是这样的,希望是有用的:

网络客户端

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"],
}
});
const io = require('socket.io')(server, {
cors: {
origin: ['https://example.com','http://example.com','ip-address'],
}
});

不要使用 来源:它是一个 安全 错误

Source 可以像数组一样使用不同的条目类型:

  • 使用类似 http/https 的协议的 URI
  • IP 地址
  • 环境变量,如 process.env.WEB _ URL

在服务器端使用以下命令:

const express = require("express");
const cors = require("cors");
const http = require("http");
 

const app = express();
app.use(express.json());
app.use(cors());
 

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

socket.on("connection", (socket) => {
console.log("socket connection : ", socket.id);
});
 

server.listen(3001, () => {
console.log("server has started!");
});