node.js, socket.io với SSL


163

Tôi đang cố gắng để socket.io chạy với chứng chỉ SSL của tôi, tuy nhiên, nó sẽ không kết nối.

Tôi dựa vào mã của mình từ ví dụ trò chuyện:

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

Nếu tôi xóa mã SSL, nó sẽ chạy tốt, tuy nhiên với nó, tôi nhận được yêu cầu http://domain.com/socket.io/1/?t=1309967919512

Lưu ý rằng nó không thử https, điều này khiến nó bị lỗi.

Tôi đang thử nghiệm trên chrome, vì đây là trình duyệt mục tiêu cho ứng dụng này.

Tôi xin lỗi nếu đây là một câu hỏi đơn giản, tôi là một newbie nút / socket.io.

Cảm ơn!


Là khách hàng của bạn đang cố gắng kết nối với URI tiền tố 'wss: //'.
kanaka

Không, nó không đến đó, nó yêu cầu domain.com/socket.io/1/?t=1309967919512 rồi chết.
Ngoài

Làm thế nào để bạn chỉ định địa chỉ để kết nối? "Domain.com" nghe có vẻ như một trình giữ chỗ trong thư viện phía máy khách socket.io. Bạn có thể đăng mã Javascript khách hàng mà bạn đang sử dụng để kết nối không?
kanaka

1
dự án nằm trên github: github.com/BCCasino/BCCasino
Ngoài

về cơ bản là vì socket.js của nó.io xử lý một cách kỳ diệu các công cụ phía máy khách, tất cả những gì bạn làm là chạy socket.connect
Ngoài

Câu trả lời:


186

Sử dụng URL bảo mật cho kết nối ban đầu của bạn, tức là thay vì "http: //" sử dụng "https: //". Nếu vận chuyển WebSocket được chọn, thì Socket.IO sẽ tự động sử dụng "wss: //" (SSL) cho kết nối WebSocket.

Cập nhật :

Bạn cũng có thể thử tạo kết nối bằng tùy chọn 'bảo mật':

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

chúng tôi làm điều này. chúng tôi goto https: // www.thebitcoinwheel.com và nó vẫn đưa ra yêu cầu http tự động, đây là một cái gì đó với mã socket.io và là điểm của câu hỏi.
Ngoài

1
Các bạn đã cứu mạng tôi! Tôi không thể tìm thấy các tùy chọn trên tài liệu.
Paulo Cesar

14
{secure: true}không cần thiết nếu bạn chỉ định 'https' trong url. Đây là một đoạn trích từ nguồn máy khách socket.io secure: 'https' == uri.protocol(phiên bản 0.9.16), nó đặt tùy chọn bảo mật thành true nếu https được phát hiện trong url.
XiaoChuan Yu

4
Tôi đã thử điều này với một URL https và thực sự {secure: true}không bắt buộc phải hoạt động chính xác.
D Coetzee

4
Tôi tin rằng sẽ là khôn ngoan để đảm bảo rằng kết nối được bảo mật bằng cách sử dụng cả an toàn: đúng và phát hành url https cho phía khách hàng. Bằng cách này, không có vấn đề gì bạn biết nó sẽ là một kết nối an toàn.
gabeio

53

Đây là cách tôi quản lý để thiết lập nó với express:

var fs = require( 'fs' );
var app = require('express')();
var https        = require('https');
var server = https.createServer({
    key: fs.readFileSync('./test_key.key'),
    cert: fs.readFileSync('./test_cert.crt'),
    ca: fs.readFileSync('./test_ca.crt'),
    requestCert: false,
    rejectUnauthorized: false
},app);
server.listen(8080);

var io = require('socket.io').listen(server);

io.sockets.on('connection',function (socket) {
    ...
});

app.get("/", function(request, response){
    ...
})


Tôi hy vọng rằng điều này sẽ tiết kiệm thời gian của ai đó.

Cập nhật: đối với những người sử dụng cho phép mã hóa sử dụng điều này

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

2
Đây là giải pháp duy nhất làm việc cho tôi. Cảm ơn vì đã tiết kiệm thời gian của tôi.
Francisco Hodge

làm việc tốt cho tôi sau một chút thử nghiệm và lỗi với các certs
RozzA

3
Giải pháp này đã làm việc hoàn hảo cho tôi, cảm ơn. Nếu bạn đang sử dụng các certs miễn phí từ allowencrypt.org thì bạn có thể sử dụng mã sau đây .. 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);
Hugo Rune

2
Cảm ơn rất nhiều cho câu trả lời này. Nó đã giúp tôi rất nhiều.
Harsha Jasti

2
Cảm ơn, đã làm việc như một cơ duyên với các tập tin letencrypt và .pem
Eric

33

Cùng một lưu ý, nếu máy chủ của bạn hỗ trợ cả hai httphttpsbạn có thể kết nối bằng:

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

để tự động phát hiện sơ đồ trình duyệt và kết nối bằng http / https tương ứng. khi ở https, việc vận chuyển sẽ được bảo mật theo mặc định, khi kết nối bằng

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

sẽ sử dụng các ổ cắm web an toàn - wss://( {secure: true}là dự phòng).

để biết thêm thông tin về cách phục vụ cả http và https một cách dễ dàng bằng cách sử dụng cùng một máy chủ nút, hãy xem câu trả lời này .


10

Nếu tệp chứng nhận máy chủ của bạn không đáng tin cậy, (ví dụ: bạn có thể tự tạo kho khóa bằng lệnh keytool trong java), bạn nên thêm tùy chọn bổ sung từ chốiUnauthorized

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

sẽ được đánh giá cao nếu bạn thêm một ví dụ giải thích cách bạn sử dụng keytool để tạo khóa đó cho nút. Vì các khóa rất phức tạp và không có đủ hướng dẫn về nó.
bvdb

keytool là một công cụ bên trong Bộ công cụ phát triển Java (JDK). bạn có thể tham khảo tài liệu này.oracle.com / javase / 10 / tools / từ
clevertension

4

kiểm tra cái này

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

2

Phía máy chủ:

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

Phía khách hàng:

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

2

Tùy thuộc vào nhu cầu của bạn, bạn có thể cho phép cả kết nối an toàn và không bảo mật và vẫn chỉ sử dụng một phiên bản Socket.io.

Bạn chỉ cần cài đặt hai máy chủ, một cho HTTP và một cho HTTPS, sau đó đính kèm các máy chủ đó vào ví dụ Socket.io.

Phía máy chủ:

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

});

Phía khách hàng :

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

Nếu máy chủ NodeJS của bạn khác với máy chủ Web của bạn, bạn có thể cần phải đặt một số tiêu đề CORS. Vì vậy, ở phía máy chủ, thay thế:

httpServer.listen( httpPort, function() {
    console.log(  `Listening HTTP on ${httpPort}` );
});
httpsServer.listen( httpsPort, function() {
    console.log(  `Listening HTTPS on ${httpsPort}` );
});

Với:

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

Và tất nhiên điều chỉnh các giá trị của các tiêu đề theo nhu cầu của bạn.


1

Đối với các ứng dụng doanh nghiệp, cần lưu ý rằng bạn không nên xử lý https trong mã của mình. Nó sẽ được tự động nâng cấp qua IIS hoặc nginx. Ứng dụng không nên biết về những giao thức được sử dụng.


0

Đây là tập tin cấu hình nginx và mã iosocket của tôi. Máy chủ (express) đang nghe trên cổng 9191. Nó hoạt động tốt: tập tin cấu hình 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;

}

Người phục vụ:

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

Khách hàng (phản ứng):

    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)
        })
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.