node.js, socket.io พร้อม SSL


163

ฉันกำลังพยายามให้ socket.io ทำงานด้วยใบรับรอง SSL ของฉัน แต่จะไม่เชื่อมต่อ

ฉันใช้รหัสจากตัวอย่างแชท:

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 เนื่องจากเป็นเบราว์เซอร์เป้าหมายสำหรับแอปพลิเคชันนี้

ฉันขอโทษถ้านี่เป็นคำถามง่าย ๆ ฉันเป็นปม / socket.io มือใหม่

ขอบคุณ!


ไคลเอ็นต์ของคุณพยายามเชื่อมต่อกับ 'wss: //' คำนำหน้า URI หรือไม่
kanaka

ไม่มันอยู่ที่นั่นมันทำให้การร้องขอไปที่ domain.com/socket.io/1/?t=1309967919512จากนั้นก็ตาย
เกิน

คุณระบุที่อยู่เพื่อเชื่อมต่อได้อย่างไร "domain.com" ดูเหมือนว่าตัวยึดตำแหน่งในไลบรารีฝั่งไคลเอ็นต์ socket.io คุณสามารถโพสต์รหัส Javascript ที่ลูกค้าใช้ในการเชื่อมต่อได้หรือไม่?
kanaka

1
โครงการตั้งอยู่บน GitHub: github.com/BCCasino/BCCasino
นอกเหนือจาก

โดยพื้นฐานแล้วเพราะ socket.js ของมัน socket.io อย่างน่าอัศจรรย์จัดการสิ่งที่ลูกค้าด้านทั้งหมดที่คุณทำคือเรียกใช้ socket.connect
เกิน

คำตอบ:


186

ใช้ URL ที่ปลอดภัยสำหรับการเชื่อมต่อเริ่มต้นของคุณเช่นแทนที่จะเป็น "http: //" ใช้ "https: //" หากเลือกการขนส่ง WebSocket แล้ว Socket.IO ควรใช้ "wss: //" (SSL) สำหรับการเชื่อมต่อ WebSocket โดยอัตโนมัติเช่นกัน

อัปเดต :

คุณสามารถลองสร้างการเชื่อมต่อโดยใช้ตัวเลือก 'ปลอดภัย':

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

เราทำสิ่งนี้ เราไปที่ https: // www.thebitcoinwheel.com และมันยังคงร้องขอ http โดยอัตโนมัตินี่คือสิ่งที่มีรหัส socket.io และเป็นประเด็นของคำถาม
เกิน

1
พวกคุณช่วยชีวิตฉันไว้! ฉันไม่พบตัวเลือกเหล่านั้นในเอกสาร
Paulo Cesar

14
{secure: true}ไม่ควรจำเป็นถ้าคุณระบุ 'https' ใน URL นี่คือข้อความที่ตัดตอนมาจากแหล่งที่มาของไคลเอ็นต์ socket.io secure: 'https' == uri.protocol(เวอร์ชัน 0.9.16) ตั้งค่าตัวเลือกที่ปลอดภัยเป็นจริงหากตรวจพบ https ใน url
XiaoChuan Yu

4
ฉันลองมันด้วย https URL และแน่นอน{secure: true}ไม่จำเป็นต้องทำงานอย่างถูกต้อง
D Coetzee

4
ฉันเชื่อว่าจะเป็นการระมัดระวังที่จะรับรองว่าการเชื่อมต่อนั้นปลอดภัยโดยใช้ทั้งการรักษาความปลอดภัย: จริงและการออก url ของ https ไปยังฝั่งไคลเอ็นต์ วิธีนี้ไม่ว่าคุณจะรู้ว่าอะไรจะเป็นการเชื่อมต่อที่ปลอดภัย
gabeio

53

นี่คือวิธีที่ฉันจัดการเพื่อตั้งค่าด้วย 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){
    ...
})


ฉันหวังว่านี่จะช่วยประหยัดเวลาของใครบางคน

อัปเดต:สำหรับผู้ที่ใช้ให้ใช้การเข้ารหัสนี้

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

2
นี่เป็นทางออกเดียวที่ได้ผลสำหรับฉัน ขอบคุณที่ช่วยประหยัดเวลา
Francisco Hodge

ใช้งานได้ดีสำหรับฉันหลังจากการทดลองและข้อผิดพลาดเล็กน้อยกับ
Roerts

3
วิธีนี้ใช้ได้ผลดีสำหรับฉันขอบคุณ หากคุณกำลังใช้ certs ฟรีจาก 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);
Hugo Rune

2
ขอบคุณมากสำหรับคำตอบนี้ มันช่วยฉันได้มาก
Harsha Jasti

2
ขอขอบคุณทำงานเหมือนจับใจกับไฟล์ letencrypt และ. pem
Eric

33

ในบันทึกเดียวกันหากเซิร์ฟเวอร์ของคุณรองรับทั้งสองhttpและhttpsคุณสามารถเชื่อมต่อโดยใช้:

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

เพื่อตรวจจับเบราว์เซอร์แบบอัตโนมัติและเชื่อมต่อโดยใช้ http / https ตามลำดับ เมื่ออยู่ใน https การขนส่งจะถูกรักษาความปลอดภัยโดยค่าเริ่มต้นเช่นเดียวกับการเชื่อมต่อโดยใช้

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

จะใช้ซ็อกเก็ตเว็บที่ปลอดภัย - wss://( {secure: true}ซ้ำซ้อน)

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการให้บริการทั้ง http และ https ได้อย่างง่ายดายโดยใช้โหนดเซิร์ฟเวอร์เดียวกันลองดูคำตอบนี้


10

หากไฟล์รับรองเซิร์ฟเวอร์ของคุณไม่น่าเชื่อถือ (ตัวอย่างเช่นคุณอาจสร้างที่เก็บคีย์ด้วยตัวเองด้วยคำสั่งkeytoolใน java) คุณควรเพิ่มตัวเลือกพิเศษที่ไม่ได้รับอนุญาต

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

จะนิยมถ้าคุณเพิ่มตัวอย่างอธิบายวิธีการที่คุณใช้ keytool เพื่อสร้างคีย์นั้นสำหรับโหนด 'ปุ่มสาเหตุซับซ้อนมากและมีบทเรียนไม่เพียงพอเกี่ยวกับเรื่องนี้
bvdb

keytool เป็นเครื่องมือภายใน Java Development Kit (JDK) คุณสามารถอ้างอิงdocs.oracle.com/javase/10/tools/
Clevertension

4

ตรวจสอบนี้กำหนดค่า ..

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

ฝั่งเซิร์ฟเวอร์:

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>

2

ขึ้นอยู่กับความต้องการของคุณคุณสามารถอนุญาตการเชื่อมต่อที่ปลอดภัยและไม่ปลอดภัยและยังคงใช้เพียงหนึ่ง 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 ของคุณแตกต่างจากเว็บเซิร์ฟเวอร์ของคุณคุณอาจต้องตั้งค่าส่วนหัว CORS ดังนั้นในฝั่งเซิร์ฟเวอร์ให้แทนที่:

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

ด้วย:

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

และแน่นอนปรับค่าของส่วนหัวตามความต้องการของคุณ


1

สำหรับแอปพลิเคชันระดับองค์กรควรสังเกตว่าคุณไม่ควรจัดการ https ในรหัสของคุณ ควรอัปเกรดอัตโนมัติผ่าน IIS หรือ nginx แอปไม่ควรรู้เกี่ยวกับโปรโตคอลที่ใช้


0

นี่คือไฟล์กำหนดค่า nginx ของฉันและรหัส iosocket เซิร์ฟเวอร์ (ด่วน) กำลังฟังพอร์ต 9191 ทำงานได้ดี: ไฟล์ config 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)
        })
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.