node.js, socket.io dengan SSL


163

Saya mencoba menjalankan socket.io dengan sertifikat SSL saya, namun itu tidak akan terhubung.

Saya mendasarkan kode saya pada contoh obrolan:

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

Jika saya menghapus kode SSL itu berjalan dengan baik, namun dengan itu saya mendapatkan permintaan untuk http://domain.com/socket.io/1/?t=1309967919512

Perhatikan itu tidak mencoba https, yang menyebabkannya gagal.

Saya menguji pada chrome, karena itu adalah browser target untuk aplikasi ini.

Saya minta maaf jika ini adalah pertanyaan sederhana, saya seorang pemula node / socket.io.

Terima kasih!


Apakah klien Anda mencoba terhubung ke URI awalan 'wss: //'.
kanaka

tidak sampai di sana, ia membuat permintaan ke domain.com/socket.io/1/?t=1309967919512 lalu mati.
Melampaui

Bagaimana Anda menentukan alamat yang akan dihubungkan? "domain.com" terdengar seperti tempat di pustaka sisi klien socket.io. Bisakah Anda memposting kode Javascript klien Anda yang Anda gunakan untuk terhubung?
kanaka

1
proyek ada di github: github.com/BCCasino/BCCasino
Beyond

pada dasarnya karena itu node.js socket.io ajaib menangani hal-hal sisi klien, semua yang Anda lakukan adalah menjalankan socket.connect
Beyond

Jawaban:


186

Gunakan URL aman untuk koneksi awal Anda, yaitu sebagai ganti "http: //" use "https: //". Jika transport WebSocket dipilih, maka Socket.IO harus secara otomatis menggunakan "wss: //" (SSL) untuk koneksi WebSocket juga.

Perbarui :

Anda juga dapat mencoba membuat koneksi menggunakan opsi 'aman':

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

kami melakukan ini. kami pergi https: // www.thebitcoinwheel.com dan masih membuat permintaan ke http secara otomatis, ini adalah sesuatu dengan kode socket.io dan merupakan inti dari pertanyaan.
Melampaui

1
Kalian menyelamatkan hidupku! Saya tidak dapat menemukan opsi itu di dokumentasi.
Paulo Cesar

14
{secure: true}seharusnya tidak diperlukan jika Anda menentukan 'https' di url. Berikut adalah kutipan dari sumber klien socket.io secure: 'https' == uri.protocol(versi 0.9.16), ini menetapkan opsi aman untuk true jika https terdeteksi di url.
XiaoChuan Yu

4
Saya mencoba ini dengan URL https dan memang {secure: true}tidak diperlukan untuk berfungsi dengan benar.
D Coetzee

4
Saya percaya akan lebih bijaksana untuk memastikan bahwa koneksi aman dengan menggunakan secure: true dan mengeluarkan https url ke sisi klien. Dengan cara ini apa pun yang Anda tahu itu akan menjadi koneksi yang aman.
gabeio

53

Beginilah cara saya mengaturnya dengan 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){
    ...
})


Saya berharap ini akan menghemat waktu seseorang.

Pembaruan: bagi yang menggunakan memungkinkan enkripsi gunakan ini

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

2
Ini adalah satu-satunya solusi yang berhasil untuk saya. Terima kasih telah menghemat waktu saya.
Francisco Hodge

bekerja baik untuk saya setelah sedikit trial and error dengan sertifikat
RozzA

3
Solusi ini bekerja sempurna untuk saya, terima kasih. Jika Anda menggunakan sertifikat gratis dari letsencrypt.org maka Anda dapat menggunakan kode berikut .. 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
Terima kasih banyak atas jawaban ini. Ini sangat membantu saya.
Harsha Jasti

2
Terima kasih, bekerja seperti pesona dengan letsencrypt dan file .pem
Eric

33

Pada catatan yang sama, jika server Anda mendukung keduanya httpdan httpsAnda dapat terhubung menggunakan:

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

untuk secara otomatis mendeteksi skema browser dan terhubung menggunakan http / https sesuai. ketika di https, transpor akan diamankan secara default, seperti menghubungkan menggunakan

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

akan menggunakan soket web yang aman - wss://( {secure: true}berlebihan).

untuk informasi lebih lanjut tentang cara melayani http dan https dengan mudah menggunakan server simpul yang sama, periksa jawaban ini .


10

Jika file sertifikat server Anda tidak tepercaya, (misalnya, Anda dapat membuat sendiri keystore dengan perintah keytool di java), Anda harus menambahkan opsi tambahan rejectUnauthorized

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

akan dihargai jika Anda menambahkan contoh yang menjelaskan bagaimana Anda menggunakan keytool untuk membuat kunci itu untuk simpul. Karena kunci sangat rumit dan tidak ada cukup tutorial tentangnya.
bvdb

keytool adalah alat di dalam Java Development Kit (JDK). Anda dapat mereferensikan docs.oracle.com/javase/10/tools/…
clevertension

4

periksa konfigurasi ini ..

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

Sisi server:

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

Sisi klien:

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

Tergantung pada kebutuhan Anda, Anda dapat mengizinkan koneksi aman dan tidak aman dan masih hanya menggunakan satu contoh Socket.io.

Anda cukup menginstalasi dua server, satu untuk HTTP dan satu untuk HTTPS, kemudian melampirkan server-server itu ke instance Socket.io.

Sisi server :

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

});

Sisi klien :

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

Jika server NodeJS Anda berbeda dari server Web Anda, Anda mungkin perlu mengatur beberapa header CORS. Jadi di sisi server, ganti:

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

Dengan:

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

Dan tentu saja menyesuaikan nilai header sesuai dengan kebutuhan Anda.


1

Untuk aplikasi perusahaan, harus dicatat bahwa Anda tidak boleh menangani https dalam kode Anda. Itu harus otomatis ditingkatkan melalui IIS atau nginx. Aplikasi seharusnya tidak tahu tentang protokol apa yang digunakan.


0

Ini adalah file konfigurasi nginx saya dan kode iosocket. Server (express) mendengarkan pada port 9191. Ia bekerja dengan baik: file konfigurasi 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')
})

Klien (bereaksi):

    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)
        })
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.