带 SSL 的 node.js、 socket.io

我试图让 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.example/socket.io/1/?t=1309967919512的请求

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

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

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

235318 次浏览

为你的初始连接使用一个安全的 URL,也就是说,不要使用“ http://”而是使用“ https://”。如果选择了 WebSocket 传输,那么 Socket.IO 也应该自动对 WebSocket 连接使用“ wss://”(SSL)。

更新 :

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

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

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

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

当使用 https 时,默认情况下将保护传输,因为连接使用

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

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

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

下面是我如何设置 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...
})
   

更新: 针对那些使用 let 加密的用户

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

如果您的服务器证书文件不受信任(例如,您可以使用 java 中的 键盘工具命令自己生成密钥存储库) ,那么您应该添加额外的选项 rejectUnauthority

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

看看这个,配置。

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

服务器端:

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>

这是我的 nginx 配置文件和 iosocket 代码。服务器(快速)正在监听端口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)
})


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

根据您的需要,您可以同时允许安全连接和不安全连接,并且仍然只使用一个 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 );

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

服务器端:

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

客户端(没有内置的 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>

以防有人需要更短的表格

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

我需要让它与 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.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');
}

客户端:

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