【问题标题】:node.js, socket.io with SSL带有 SSL 的 node.js、socket.io
【发布时间】:2011-09-29 18:41:45
【问题描述】:

我正在尝试使用我的 SSL 证书运行 socket.io,但是它无法连接。

我的代码基于聊天示例:

var https = require('https');
var fs = require('fs');
/**
 * Bootstrap app.
 */
var sys = require('sys')
require.paths.unshift(__dirname + '/../../lib/');

/**
* Module dependencies.
*/

var express = require('express')
  , stylus = require('stylus')
  , nib = require('nib')
  , sio = require('socket.io');

/**
 * App.
 */
var privateKey = fs.readFileSync('../key').toString();
var certificate = fs.readFileSync('../crt').toString();
var ca = fs.readFileSync('../intermediate.crt').toString();

var app = express.createServer({key:privateKey,cert:certificate,ca:ca });


/**
 * App configuration.
 */

...

/**
 * App routes.
 */

app.get('/', function (req, res) {
  res.render('index', { layout: false });
});

/**
 * App listen.
 */

app.listen(443, function () {
  var addr = app.address();
  console.log('   app listening on http://' + addr.address + ':' + addr.port);
});

/**
 * Socket.IO server (single process only)
 */

var io = sio.listen(app,{key:privateKey,cert:certificate,ca:ca});
...

如果我删除 SSL 代码,它运行良好,但是有了它,我收到了对 http://domain.com/socket.io/1/?t=1309967919512 的请求

注意它没有尝试 https,这会导致它失败。

我正在 chrome 上进行测试,因为它是这个应用程序的目标浏览器。

如果这是一个简单的问题,我深表歉意,我是 node/socket.io 新手。

谢谢!

【问题讨论】:

  • 您的客户端是否正在尝试连接到 'wss://' 前缀的 URI。
  • 不,它没有到达那里,它向domain.com/socket.io/1/?t=1309967919512发出请求然后死掉。
  • 您如何指定要连接的地址? “domain.com”听起来像是 socket.io 客户端库中的占位符。您可以发布您用于连接的客户端 Javascript 代码吗?
  • 项目在github上:github.com/BCCasino/BCCasino
  • 基本上是因为它的 node.js socket.io 神奇地处理了客户端的东西,你所要做的就是运行 socket.connect

标签: ssl node.js https websocket socket.io


【解决方案1】:

为您的初始连接使用安全 URL,即使用“https://”代替“http://”。如果选择了 WebSocket 传输,那么 Socket.IO 也应该自动使用 "wss://" (SSL) 进行 WebSocket 连接。

更新

您也可以尝试使用“安全”选项创建连接:

var socket = io.connect('https://localhost', {secure: true});

【讨论】:

  • 我们这样做。我们转到 https : // www.thebitcoinwheel.com 它仍然自动向 http 发出请求,这是 socket.io 代码的问题,也是问题的重点。
  • {secure: true} 如果您在 url 中指定“https”,则不需要。这是来自 socket.io 客户端源 secure: 'https' == uri.protocol(版本 0.9.16)的摘录,如果在 url 中检测到 https,它会将安全选项设置为 true。
  • 我用 https URL 尝试过这个,确实{secure: true} 不需要正常工作。
  • 我相信通过同时使用secure:true和向客户端发出https url来确保连接是安全的是谨慎的。这样,无论您知道什么,这都是一个安全的连接。
【解决方案2】:

同样,如果您的服务器同时支持httphttps,您可以使用以下方式进行连接:

var socket = io.connect('//localhost');

auto detect the browser scheme 并相应地使用 http/https 进行连接。在 https 中时,默认情况下传输将受到保护,因为使用连接

var socket = io.connect('https://localhost');

将使用安全网络套接字 - wss://{secure: true} 是多余的)。

有关如何使用同一节点服务器轻松同时提供 http 和 https 的更多信息,请查看this answer

【讨论】:

    【解决方案3】:

    以下是我使用 express 设置的方法:

        var app = require('express')();
        var https = require('https');
        var fs = require( 'fs' );
        var io = require('socket.io')(server);
    
        var options = {
            key: fs.readFileSync('./test_key.key'),
            cert: fs.readFileSync('./test_cert.crt'),
            ca: fs.readFileSync('./test_ca.crt'),
    
            requestCert: false,
            rejectUnauthorized: false
        }
    
        var server = https.createServer(options, app);
        server.listen(8080);
        
    
        
        io.sockets.on('connection', function (socket) {
            // code goes here...
        });
        
        app.get("/", function(request, response){
            // code goes here...
        })
       
    

    更新:对于那些使用让我们加密的人使用这个

    var server = https.createServer({ 
                    key: fs.readFileSync('privkey.pem'),
                    cert: fs.readFileSync('fullchain.pem') 
                 }, app);
    

    【讨论】:

    • 这是唯一对我有用的解决方案。感谢您节省了我的时间。
    • 这个解决方案非常适合我,谢谢。如果您使用的是letsencrypt.org的免费证书,那么您可以使用以下代码.. var server = https.createServer({ key: fs.readFileSync('/etc/letsencrypt/live/domain.name/privkey.pem'), cert: fs.readFileSync('/etc/letsencrypt/live/domain.name/cert.pem'), ca: fs.readFileSync('/etc/letsencrypt/live/domain.name/chain.pem'), requestCert: false, rejectUnauthorized: false },app); server.listen(3000);
    • 非常感谢您的回答。这对我帮助很大。
    • "rejectUnauthorized: false 警告:它会让您容易受到 MITM 攻击!"
    • 谢谢,使用letsencrypt和.pem文件就像一个魅力
    【解决方案4】:

    如果您的服务器证书文件不受信任(例如,您可以在 java 中使用 keytool 命令自行生成密钥库),则应添加额外的选项 rejectUnauthorized

    var socket = io.connect('https://localhost', {rejectUnauthorized: false});
    

    【讨论】:

    • 如果您添加了一个示例来解释您如何使用 keytool 为节点创建该密钥,我们将不胜感激。因为钥匙太复杂了,而且没有足够的教程。
    • keytool 是 Java 开发工具包 (JDK) 中的一个工具。你可以参考这个docs.oracle.com/javase/10/tools/…
    【解决方案5】:

    检查 this.configuration..

    app = module.exports = express();
    var httpsOptions = { key: fs.readFileSync('certificates/server.key'), cert: fs.readFileSync('certificates/final.crt') };        
    var secureServer = require('https').createServer(httpsOptions, app);
    io = module.exports = require('socket.io').listen(secureServer,{pingTimeout: 7000, pingInterval: 10000});
    io.set("transports", ["xhr-polling","websocket","polling", "htmlfile"]);
    secureServer.listen(3000);
    

    【讨论】:

      【解决方案6】:

      服务器端:

      import http from 'http';
      import https from 'https';
      import SocketIO, { Socket } from 'socket.io';
      import fs from 'fs';
      import path from 'path';
      
      import { logger } from '../../utils';
      
      const port: number = 3001;
      
      const server: https.Server = https.createServer(
        {
          cert: fs.readFileSync(path.resolve(__dirname, '../../../ssl/cert.pem')),
          key: fs.readFileSync(path.resolve(__dirname, '../../../ssl/key.pem'))
        },
        (req: http.IncomingMessage, res: http.ServerResponse) => {
          logger.info(`request.url: ${req.url}`);
      
          let filePath = '.' + req.url;
          if (filePath === './') {
            filePath = path.resolve(__dirname, './index.html');
          }
      
          const extname = String(path.extname(filePath)).toLowerCase();
          const mimeTypes = {
            '.html': 'text/html',
            '.js': 'text/javascript',
            '.json': 'application/json'
          };
      
          const contentType = mimeTypes[extname] || 'application/octet-stream';
      
          fs.readFile(filePath, (error: NodeJS.ErrnoException, content: Buffer) => {
            if (error) {
              res.writeHead(500);
              return res.end(error.message);
            }
            res.writeHead(200, { 'Content-Type': contentType });
            res.end(content, 'utf-8');
          });
        }
      );
      
      const io: SocketIO.Server = SocketIO(server);
      
      io.on('connection', (socket: Socket) => {
        socket.emit('news', { hello: 'world' });
        socket.on('updateTemplate', data => {
          logger.info(data);
          socket.emit('updateTemplate', { random: data });
        });
      });
      
      server.listen(port, () => {
        logger.info(`Https server is listening on https://localhost:${port}`);
      });
      

      客户端:

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Websocket Secure Connection</title>
      </head>
      
      <body>
        <div>
          <button id='btn'>Send Message</button>
          <ul id='messages'></ul>
        </div>
        <script src='../../../node_modules/socket.io-client/dist/socket.io.js'></script>
        <script>
          window.onload = function onload() {
            const socket = io('https://localhost:3001');
            socket.on('news', function (data) {
              console.log(data);
            });
      
            socket.on('updateTemplate', function onUpdateTemplate(data) {
              console.log(data)
              createMessage(JSON.stringify(data));
            });
            const $btn = document.getElementById('btn');
            const $messages = document.getElementById('messages');
      
            function sendMessage() {
              socket.emit('updateTemplate', Math.random());
            }
      
            function createMessage(msg) {
              const $li = document.createElement('li');
              $li.textContent = msg;
              $messages.appendChild($li);
            }
      
            $btn.addEventListener('click', sendMessage);
          }
        </script>
      </body>
      
      </html>
      

      【讨论】:

        【解决方案7】:

        这是我的 nginx 配置文件和 iosocket 代码。 Server(express) 正在侦听端口 9191。它运行良好: nginx配置文件:

        server {
            listen       443 ssl;
            server_name  localhost;
            root   /usr/share/nginx/html/rdist;
        
            location /user/ {
                proxy_pass   http://localhost:9191;
            }
            location /api/ {
                proxy_pass   http://localhost:9191;
            }
            location /auth/ {
                proxy_pass   http://localhost:9191;
            }
        
            location / {
                index  index.html index.htm;
                if (!-e $request_filename){
                  rewrite ^(.*)$ /index.html break;
                }
            }
            location /socket.io/ {
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection "upgrade";
                proxy_pass   http://localhost:9191/socket.io/;
            }
        
        
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   /usr/share/nginx/html;
            }
        
            ssl_certificate /etc/nginx/conf.d/sslcert/xxx.pem;
            ssl_certificate_key /etc/nginx/conf.d/sslcert/xxx.key;
        
        }
        
        

        服务器:

        const server = require('http').Server(app)
        const io = require('socket.io')(server)
        io.on('connection', (socket) => {
            handleUserConnect(socket)
        
          socket.on("disconnect", () => {
           handleUserDisConnect(socket)
          });
        })
        
        server.listen(9191, function () {
          console.log('Server listening on port 9191')
        })
        
        

        客户(反应):

            const socket = io.connect('', { secure: true, query: `userId=${this.props.user._id}` })
        
                socket.on('notifications', data => {
                    console.log('Get messages from back end:', data)
                    this.props.mergeNotifications(data)
                })
        
        

        【讨论】:

          【解决方案8】:

          对于企业应用程序,应注意您不应在代码中处理 https。它应该通过 IIS 或 nginx 自动升级。应用不应该知道使用了哪些协议。

          【讨论】:

            【解决方案9】:

            根据您的需要,您可以允许安全和不安全的连接,但仍然只使用一个 Socket.io 实例。

            您只需实例化两台服务器,一台用于 HTTP,一台用于 HTTPS,然后将这些服务器附加到 Socket.io 实例。

            服务器端:

            // needed to read certificates from disk
            const fs          = require( "fs"    );
            
            // Servers with and without SSL
            const http        = require( "http"  )
            const https       = require( "https" );
            const httpPort    = 3333;
            const httpsPort   = 3334;
            const httpServer  = http.createServer();
            const httpsServer = https.createServer({
                "key" : fs.readFileSync( "yourcert.key" ),
                "cert": fs.readFileSync( "yourcert.crt" ),
                "ca"  : fs.readFileSync( "yourca.crt"   )
            });
            httpServer.listen( httpPort, function() {
                console.log(  `Listening HTTP on ${httpPort}` );
            });
            httpsServer.listen( httpsPort, function() {
                console.log(  `Listening HTTPS on ${httpsPort}` );
            });
            
            // Socket.io
            const ioServer = require( "socket.io" );
            const io       = new ioServer();
            io.attach( httpServer  );
            io.attach( httpsServer );
            
            io.on( "connection", function( socket ) {
            
                console.log( "user connected" );
                // ... your code
            
            });
            

            客户端:

            var url    = "//example.com:" + ( window.location.protocol == "https:" ? "3334" : "3333" );
            var socket = io( url, {
                // set to false only if you use self-signed certificate !
                "rejectUnauthorized": true
            });
            socket.on( "connect", function( e ) {
                console.log( "connect", e );
            });
            

            如果您的 NodeJS 服务器与您的 Web 服务器不同,您可能需要设置一些 CORS 标头。所以在服务器端,替换:

            const httpServer  = http.createServer();
            const httpsServer = https.createServer({
                "key" : fs.readFileSync( "yourcert.key" ),
                "cert": fs.readFileSync( "yourcert.crt" ),
                "ca"  : fs.readFileSync( "yourca.crt"   )
            });
            

            与:

            const CORS_fn = (req, res) => {
                res.setHeader( "Access-Control-Allow-Origin"     , "*"    );
                res.setHeader( "Access-Control-Allow-Credentials", "true" );
                res.setHeader( "Access-Control-Allow-Methods"    , "*"    );
                res.setHeader( "Access-Control-Allow-Headers"    , "*"    );
                if ( req.method === "OPTIONS" ) {
                    res.writeHead(200);
                    res.end();
                    return;
                }
            };
            const httpServer  = http.createServer( CORS_fn );
            const httpsServer = https.createServer({
                    "key" : fs.readFileSync( "yourcert.key" ),
                    "cert": fs.readFileSync( "yourcert.crt" ),
                    "ca"  : fs.readFileSync( "yourca.crt"   )
            }, CORS_fn );
            

            当然还有添加/删除标题并根据您的需要设置标题的值。

            【讨论】:

              【解决方案10】:

              服务器端:

              var ssl_options = {
                  ca: [fs.readFileSync('../ssl/cert1.crt'), fs.readFileSync('../ssl/cert2.crt'), fs.readFileSync('../ssl/cert3.crt')],
                  key: fs.readFileSync('../ssl/xxx.key'),
                  cert: fs.readFileSync('../ssl/xxx.com.crt'),
              };
              var wssServer = https.createServer(ssl_options,app);  // Express app
              wssServer.listen(4433, '0.0.0.0');
              global.io = require("socket.io")();
              io.listen(wssServer);
              io.on( "connection", function( socket ) {
                  console.log( "user connected" );
              });
              

              客户端(内置 WebSocket API 运气不佳):

              <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.js"></script>
              <script>
                 const socket = io("https://xxx.co:4433",{ transports: ['websocket', 'polling', 'flashsocket'] });
              </script>
              

              【讨论】:

                【解决方案11】:

                如果有人需要更短的表格

                var fs = require('fs');
                var https = require('https');
                
                var express = require('express');
                var app = express();
                
                var options = {
                  key: fs.readFileSync('/path-to/ssl.key'),
                  cert: fs.readFileSync('/path-to/ssl.cert')
                };
                
                var server = https.createServer(options, app);
                var io = require('socket.io')(server);
                

                【讨论】:

                  【解决方案12】:

                  我需要让它与 Debian 10、ISPConfig 3 和 Let's Encrypt 一起工作。我花了一段时间才弄清楚细节。也许这可以节省其他人一些时间……

                  服务器端:

                  const fs = require('fs');
                  const https = require('https');
                  const express = require('express');
                  const socketio = require('socket.io');
                  const app = express();
                  const https_options = {
                      key: fs.readFileSync('/var/www/clients/client1/web1/ssl/your-domain.com-le.key'),
                      cert: fs.readFileSync('/var/www/clients/client1/web1/ssl/your-domain.com-le.crt'),
                      ca: fs.readFileSync('/root/.acme.sh/your-domain.com/fullchain.cer'),
                      requestCert: false,
                      rejectUnauthorized: false
                  }
                  const server = https.createServer(https_options, app);
                  server.listen(3000, () => {
                      console.log('server started ok');
                  });
                  const io = socketio(server, {
                      cors: {
                          origin: "https://your-domain.com",
                      },
                      secure: true
                  });
                  io.on('connection', (sock) => {
                      console.log('someone connected');
                  }
                  

                  客户端:

                  const sock = io('https://your-domain.com:3000/');    
                  sock.on('status', (text) => {
                      add_text($('#status'), text);
                  });
                  

                  【讨论】:

                    猜你喜欢
                    • 2016-06-06
                    • 2017-07-27
                    • 2015-04-01
                    • 2014-11-29
                    • 2012-09-19
                    • 2013-05-27
                    • 1970-01-01
                    • 2013-11-24
                    • 1970-01-01
                    相关资源
                    最近更新 更多