简体   繁体   English

node.js、socket.io 与 SSL

[英]node.js, socket.io with SSL

I'm trying to get socket.io running with my SSL certificate however, it will not connect.我试图让 socket.io 与我的 SSL 证书一起运行,但是它不会连接。

I based my code off the chat example:我的代码基于聊天示例:

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});
...

If I remove the SSL code it runs fine, however with it I get a request to http://domain.example/socket.io/1/?t=1309967919512如果我删除 SSL 代码它运行正常,但是我收到一个请求http://domain.example/socket.io/1/?t=1309967919512

Note it's not trying HTTPS, which causes it to fail.请注意,它没有尝试 HTTPS,这会导致它失败。

I'm testing on chrome, since it is the target browser for this application.我正在 chrome 上进行测试,因为它是这个应用程序的目标浏览器。

I apologize if this is a simple question, I'm a node/socket.io newbie.如果这是一个简单的问题,我深表歉意,我是 node/socket.io 新手。

Use a secure URL for your initial connection, ie instead of "http://" use "https://".使用安全的 URL 进行初始连接,即使用“https://”代替“http://”。 If the WebSocket transport is chosen, then Socket.IO should automatically use "wss://" (SSL) for the WebSocket connection too.如果选择了 WebSocket 传输,那么 Socket.IO 也应该自动为 WebSocket 连接使用“wss://”(SSL)。

Update :更新

You can also try creating the connection using the 'secure' option:您还可以尝试使用“安全”选项创建连接:

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

The following is how I set up to set it up with express:以下是我如何设置以使用 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...
    })
   

Update: for those using lets encrypt use this更新:对于那些使用让加密使用这个

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

On the same note, if your server supports both http and https you can connect using:同样,如果您的服务器同时支持httphttps您可以使用以下方式进行连接:

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

to auto detect the browser scheme and connect using http/https accordingly. 自动检测浏览器方案并相应地使用 http/https 进行连接。 when in https, the transport will be secured by default, as connecting using在 https 中时,默认情况下会保护传输,因为使用连接

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

will use secure web sockets - wss:// (the {secure: true} is redundant).将使用安全 web sockets - wss://{secure: true}是多余的)。

for more information on how to serve both http and https easily using the same node server check out this answer .有关如何使用同一节点服务器轻松同时服务 http 和 https 的更多信息,请查看此答案

If your server certificated file is not trusted, (for example, you may generate the keystore by yourself with keytool command in java), you should add the extra option rejectUnauthorized如果您的服务器证书文件不受信任(例如,您可以在 java 中使用keytool命令自己生成密钥库),您应该添加额外的选项 rejectUnauthorized

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

Depending on your needs, you could allow both secure and unsecure connections and still only use one Socket.io instance.根据您的需要,您可以同时允许安全连接和不安全连接,并且仍然只使用一个 Socket.io 实例。

You simply have to instanciate two servers, one for HTTP and one for HTTPS, then attach those servers to the Socket.io instance.您只需实例化两台服务器,一台用于 HTTP,另一台用于 HTTPS,然后将这些服务器附加到 Socket.io 实例。

Server side:服务器端:

// 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

});

Client side:客户端:

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

If your NodeJS server is different from your Web server, you will maybe need to set some CORS headers.如果您的 NodeJS 服务器与 Web 服务器不同,您可能需要设置一些 CORS 标头。 So in the server side, replace:所以在服务器端,替换:

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

With:和:

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 );

And of course add/remove headers and set the values of the headers according to your needs.当然,添加/删除标题并根据您的需要设置标题的值。

check this.configuration..检查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);

Server-side:服务器端:

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}`);
});

Client-side:客户端:

<!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>

For enterprise applications it should be noted that you should not be handling https in your code.对于企业应用程序,应注意您不应在代码中处理 https。 It should be auto upgraded via IIS or nginx.它应该通过 IIS 或 nginx 自动升级。 The app shouldn't know about what protocols are used.该应用程序不应该知道使用了哪些协议。

In case someone need a shorter form如果有人需要更短的表格

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);

This is my nginx config file and iosocket code.这是我的 nginx 配置文件和 iosocket 代码。 Server(express) is listening on port 9191. It works well: nginx config file:服务器(快递)正在侦听端口 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;

}

Server:服务器:

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

Client(react):客户(反应):

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

Server side:服务器端:

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.example.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" );
});

Client-side (no luck with the built-in WebSocket API):客户端(内置 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.example:4433",{ transports: ['websocket', 'polling', 'flashsocket'] });
</script>

I needed to get this to work with Debian 10, ISPConfig 3 and Let's Encrypt.我需要让它与 Debian 10、ISPConfig 3 和 Let's Encrypt 一起使用。 Took me a while to work out the specifics.我花了一段时间才弄清楚细节。 Maybe this saves someone else some time…也许这可以节省其他人一些时间……

Server-side:服务器端:

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.example/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.example",
    },
    secure: true
});
io.on('connection', (sock) => {
    console.log('someone connected');
}

Client-side:客户端:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM