【问题标题】:Socket.io + Node.js Cross-Origin Request BlockedSocket.io + Node.js 跨域请求被阻止
【发布时间】:2014-07-26 07:50:09
【问题描述】:

我正在使用 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>

我还在应用根目录中使用 .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"

【问题讨论】:

  • 你有没有得到这个工作?如果是这样,您的解决方案是什么?

标签: node.js sockets socket.io cors


【解决方案1】:

简单的服务器端修复

❗ 不要使用“socketio”包...改用“socket.io”。 “socketio”已过时。有些用户似乎使用了错误的包。

❗ 我不赞成将原点设置为 *,因为这使网络钓鱼网站能够模仿您网站的外观和感觉,然后让它以同样的方式工作。如果你设置了原点,你可以避免这种情况。

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

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

io.set('origins', '*:*');

io.origins('*:*') // for latest version

* 单独不起作用,这让我陷入了困境。

【讨论】:

  • 我使用的是最新版本,但不是,它给我的连接被拒绝错误
  • @gamer 可能会通过所有第一原则然后继续上升,例如,我的防火墙是否阻塞了端口?我可以ping我的服务器等吗?
  • 你是个天才。这是 express 的最佳解决方案!
  • 从 v3.x 开始,请参考这个 (socket.io/docs/v3/handling-cors) 和这个 (github.com/expressjs/cors#configuration-options) 链接。 ``` var io = require('socket.io')(http, { 'cors': { 'methods': ['GET', 'PATCH', 'POST', 'PUT'], 'origin': true // 从任何域接受 } }); ```
  • @vee 优秀的解决方案!
【解决方案2】:

我正在使用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);

【讨论】:

  • 我想了解为什么这对 io.origin(":") 有效,这毫无意义,我什至尝试用 socket.io 的原始处理程序覆盖它,注意到除此之外的工作.. 我正在使用 socket.io 1.7.4 和 socket.io-client 2.2.0 和 socket.io-adapter-mongodb 0.0.2
  • 非常感谢我找到了这篇文章,我今天花了 5-6 个小时尝试在我的套接字服务器上启用 CORS。我确实尝试了在stackoverflow上可以找到的每一种方法。这是唯一对我有用的方法。我不明白为什么 io.origin(":"), 在这种情况下也不起作用。我过去构建过其他套接字服务器,从未遇到过问题。如果有人有一个理论,我很想听听。无论如何,非常感谢分享这个!
【解决方案3】:

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

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

这里http://yourdomain.com:80 是您希望允许来自的请求的来源。

你可以阅读更多关于origins格式here

【讨论】:

    【解决方案4】:

    对于在此处寻找新 Socket.io (3.x) 的任何人,migration documents 非常有帮助。

    特别是这个sn-p:

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

    【讨论】:

    • 完美!现在我的套接字因 POST 请求而中断,说这是来自我的 vue socket.io 客户端的错误握手。但我认为这个新错误是进步。非常感谢!
    【解决方案5】:

    我在上面尝试过,但没有任何效果。以下代码来自socket.io documentation,它有效。

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

    【讨论】:

      【解决方案6】:

      如果你得到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: '*' } });
      

      【讨论】:

        【解决方案7】:

        我只想说,在尝试了很多东西之后,解决我的 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一起工作
        【解决方案8】:

        就我而言,我使用的是 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: '*' } });
        

        【讨论】:

          【解决方案9】:

          在阅读了 StakOverflow 和其他论坛上的大量主题后,我找到了适合我的解决方案。此解决方案适用于不使用 Express

          这里是先决条件。


          服务器端

          // 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 集成的准系统 NodeJS 服务器的最清晰示例之一;赞成
          【解决方案10】:

          好的,我在使用自签名证书进行测试时遇到了一些问题,因此我将复制对我有用的设置。如果您不使用自签名证书,您可能不会遇到这些问题,希望如此!

          首先取决于您的浏览器 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

          【讨论】:

            【解决方案11】:

            我在使用 socket.io 和 node.js & React 制作聊天应用程序时遇到问题。这个问题也不是 Firefox 浏览器的问题,我在 Edge 和 Chrome 中也遇到了同样的问题。

            “跨域请求被阻塞,被其他资源占用...”

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

            npm install cors

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

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

            【讨论】:

            • 实施后出现同样的问题。在本地主机中工作,但不在外部
            【解决方案12】:

            这可能是 Firefox 的认证问题,不一定是您的 CORS 有任何问题。 Firefox CORS request giving 'Cross-Origin Request Blocked' despite headers

            我遇到了与 Socketio 和 Nodejs 在 Firefox 中引发 CORS 错误的完全相同的问题。我有 *.myNodeSite.com 的证书,但我为 Nodejs 引用了 LAN IP 地址 192.168.1.10。 (WAN IP 地址也可能引发相同的错误。)由于证书与 IP 地址引用不匹配,Firefox 引发了该错误。

            【讨论】:

              【解决方案13】:

              这是官方文档中的解决方案:

              Since Socket.IO v3, you need to explicitly enable Cross-Origin Resource Sharing (CORS).

              const io = require("socket.io")(httpServer, {cors: {
              origin: "https://example.com", // or "*"
              methods: ["GET", "POST"]}});
              

              【讨论】:

              • 这解决了我的问题,我试图从 Angular 应用程序连接套接字并在 Angular 应用程序中使用 ngx-socket-io 模块。
              【解决方案14】:

              看看这个: Complete Example

              服务器:

              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>
              

              我记得很多天前结合stackoverflow的答案;但我找不到提及它们的主要链接

              【讨论】:

                【解决方案15】:

                因此,基本上在 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

                【讨论】:

                  【解决方案16】:

                  客户:

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

                  服务器:

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

                  它对我来说就像一个魅力。

                  【讨论】:

                  • 您能否添加更多解释?你的代码做了什么,它对 OP 有什么帮助?
                  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                  【解决方案17】:

                  我遇到了同样的问题,任何解决方案都对我有用。

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

                  我在客户端的查询参数名称有错字,所以连接总是被拒绝,但是浏览器抱怨cors...

                  当我修正错字后,它开始按预期工作,我不需要使用任何额外的东西,全局 express cors 设置就足够了。

                  因此,如果有什么对您有用,并且您正在使用 allowRequest,请检查此函数是否正常工作,因为它引发的错误会在浏览器中显示为 cors 错误。除非你在想要拒绝连接时手动添加 cors 标头,否则我猜。

                  【讨论】:

                    【解决方案18】:

                    对 socket.io 和 socket.io-client 使用相同的版本解决了我的问题。

                    【讨论】:

                      【解决方案19】:

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

                      然后你可以使用 io.set('origins', 'http://yourdomain.com:PORT_NUMBER');

                      【讨论】:

                        【解决方案20】:

                        我在 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();
                          }
                        });
                        

                        【讨论】:

                          【解决方案21】:

                          如果您的 socket.io 应用程序在 Chrome、Safari 和其他浏览器上运行,但在 Firefox 中仍然遇到 CORS 问题,并且您使用的是自签名证书,那么问题是 Firefox 不接受自签名证书默认情况下,您必须通过转到 Firefox 的首选项 > 证书 > 查看证书 > 添加例外来添加例外。

                          如果您不这样做,Firefox 会显示您发布的具有误导性的错误,但在其开发人员工具的深处,您会发现此错误:MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT。这表明 Firefox 根本不接受证书,因为它是自签名的。

                          【讨论】:

                            【解决方案22】:
                            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
                            

                            【讨论】:

                              【解决方案23】:

                              我只是将后端的 socket.io 版本从 2.x.x 更新到 4.1.2 并做了同样的事情,即。将前端的 socket.io-client 版本从 2.x.x 更新到 4.1.2 ....它工作了

                              【讨论】:

                                【解决方案24】:

                                我正在与socket.io: 4.2.xnode: 14.17.x@hapi/hapi: 20.1.x 合作。

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

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

                                请确保您在选项对象中有{ cors: { origin: '*' } }

                                【讨论】:

                                  【解决方案25】:

                                  我正在使用带有隐式 http 服务器的 SocketIO,并且我正在使用 v4.4 的套接字 io,我必须在服务器中这样做:

                                  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"
                                  })
                                  

                                  【讨论】:

                                    【解决方案26】:

                                    对于那些使用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;
                                            });
                                        }
                                    

                                    【讨论】:

                                      猜你喜欢
                                      • 2014-12-27
                                      • 2021-06-26
                                      • 2014-10-13
                                      • 2019-11-04
                                      • 2017-02-06
                                      • 2021-02-19
                                      相关资源
                                      最近更新 更多