无法与 javascript 建立连接到安全的 websocket 服务器
Can't establish a connection with javascript to a secure websocket server
我的开发环境是这样的:
- OS:微软Windows10
- PHP 框架:Laravel 8.0
- PHP 版本 7.4
- Websocket 服务器:cboden/ratchet0.4.3
- WAMP 服务器 3.2.0 (Apache 2.4.41)
- Firefox 91.0.1(64 位)/chrome
我创建了一个新的 Laravel 应用程序来实现安全 Websocket 服务器,并在客户端使用纯 javascript 连接到它(Laravel blade 文件) .
据我所知,websocket 服务器工作正常 运行,但网络浏览器无法连接,如下图所示:
我试过使用不同的 URL,有和没有端口号,但都无济于事。
我使用 openssl.exe 工具创建了一个 SSL 证书和私钥文件,并将它们放在命令文件夹中以供测试。
这是我的安全 Websocket 服务器句柄代码:
public function handle()
{
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8090', $loop),
$loop,
array(
'local_cert' => 'certificate.crt',
'local_pk' => 'private.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
// Ratchet magic
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
我在 httpd-ssl.conf
文件中的虚拟主机:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
ProxyRequests Off
ProxyPass /wss/ ws://ssa:8090
</VirtualHost>
已加载 Apache 模块 proxy_module、proxy_http_module 和 proxy_wstunnel_module。
Web 应用程序在 HTTPS 中是 运行。
之前,它是通过 HTTP 和 WS 运行 并且一切正常,但我需要保护这个应用程序并且我在连接到安全的 websocket 服务器时遇到问题。
我是不是漏掉了什么?
我的 Websocket 服务器或 Apache 配置有问题吗?
您肯定尝试连接到错误的目的地。它说 wss:///ssa/wss/,但可能应该是 wss://your.site.domain/ssa/wss/ .
所以让我们看一下前端代码,看看它有什么问题。
好的,正如@apokryfos 所指出的,我尝试通过 HTTPS 代理 websocket 服务器,但我以错误的方式进行。
我将我的 websocket 服务器更改为不安全的,并对我的虚拟主机进行了以下更改:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
Redirect /wss /wss/
ProxyPass /wss/ ws://127.0.0.1:8090/
ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
在客户端,浏览器现在可以通过HTTPS端口联系后端WS服务器:
// The connection to the WebSocket Server.
var socket = new WebSocket("wss://ssa:443/wss/");
我从
Apache Config: Websockets Proxy WSS request to WS backend
现在我通过 HTTPS 获得了我的非安全 Websocket 服务器 sending/receiving。
当然,这不是我期望应用于我的需求的解决方案,但它确实有效。
我仍然希望找到一个正式的解决方案,在不使用代理机制的情况下将普通 JavaScript 客户端连接到安全 Websocket 服务器 (wss://)。
为了不让我的第一个答案复杂化,提供更多信息,我在这里提供毕竟对我真正有用的答案。
我按如下方式创建了安全 Websocket 服务器:
public function handle() {
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8443', $loop),
$loop,
array(
'local_cert' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.crt',
'local_pk' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
注意我把端口号改成了8443(我觉得这没啥关系),也把证书和密钥文件改成了新的,生成如下:
openssl req -config config.conf -new -x509 -out server.crt -days 3650
config.conf 文件是:
[req]
default_bits = 2048
encrypt_key = no
default_md = sha256
default_keyfile = server.key
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = KH
ST = Siem Reap
L = SR
O = AHC
OU = IT
CN = localhost
[bs_section]
CA=false
所有不同之处在于最后一行 CA=false
表明我没有签署或充当证书颁发机构 (CA)。
这摆脱了 MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
消息。
然后,我删除了在我的 httpd-ssl.conf 文件中定义代理的行:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/server.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/server.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
#Redirect /wss /wss/
#ProxyPass /wss/ ws://127.0.0.1:8090/
#ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
请注意,对于这个虚拟主机,我使用了与安全 Websocket 服务器相同的证书和密钥文件。
好的,这就是我的证书问题。
现在一切正常。
我的开发环境是这样的:
- OS:微软Windows10
- PHP 框架:Laravel 8.0
- PHP 版本 7.4
- Websocket 服务器:cboden/ratchet0.4.3
- WAMP 服务器 3.2.0 (Apache 2.4.41)
- Firefox 91.0.1(64 位)/chrome
我创建了一个新的 Laravel 应用程序来实现安全 Websocket 服务器,并在客户端使用纯 javascript 连接到它(Laravel blade 文件) . 据我所知,websocket 服务器工作正常 运行,但网络浏览器无法连接,如下图所示:
我试过使用不同的 URL,有和没有端口号,但都无济于事。 我使用 openssl.exe 工具创建了一个 SSL 证书和私钥文件,并将它们放在命令文件夹中以供测试。
这是我的安全 Websocket 服务器句柄代码:
public function handle()
{
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8090', $loop),
$loop,
array(
'local_cert' => 'certificate.crt',
'local_pk' => 'private.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
// Ratchet magic
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
我在 httpd-ssl.conf
文件中的虚拟主机:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
ProxyRequests Off
ProxyPass /wss/ ws://ssa:8090
</VirtualHost>
已加载 Apache 模块 proxy_module、proxy_http_module 和 proxy_wstunnel_module。 Web 应用程序在 HTTPS 中是 运行。 之前,它是通过 HTTP 和 WS 运行 并且一切正常,但我需要保护这个应用程序并且我在连接到安全的 websocket 服务器时遇到问题。
我是不是漏掉了什么?
我的 Websocket 服务器或 Apache 配置有问题吗?
您肯定尝试连接到错误的目的地。它说 wss:///ssa/wss/,但可能应该是 wss://your.site.domain/ssa/wss/ .
所以让我们看一下前端代码,看看它有什么问题。
好的,正如@apokryfos 所指出的,我尝试通过 HTTPS 代理 websocket 服务器,但我以错误的方式进行。
我将我的 websocket 服务器更改为不安全的,并对我的虚拟主机进行了以下更改:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/certificate.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/private.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
Redirect /wss /wss/
ProxyPass /wss/ ws://127.0.0.1:8090/
ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
在客户端,浏览器现在可以通过HTTPS端口联系后端WS服务器:
// The connection to the WebSocket Server.
var socket = new WebSocket("wss://ssa:443/wss/");
我从 Apache Config: Websockets Proxy WSS request to WS backend
现在我通过 HTTPS 获得了我的非安全 Websocket 服务器 sending/receiving。 当然,这不是我期望应用于我的需求的解决方案,但它确实有效。 我仍然希望找到一个正式的解决方案,在不使用代理机制的情况下将普通 JavaScript 客户端连接到安全 Websocket 服务器 (wss://)。
为了不让我的第一个答案复杂化,提供更多信息,我在这里提供毕竟对我真正有用的答案。
我按如下方式创建了安全 Websocket 服务器:
public function handle() {
$loop = Factory::create();
$webSock = new SecureServer(
new Server('0.0.0.0:8443', $loop),
$loop,
array(
'local_cert' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.crt',
'local_pk' => 'C:/wamp64/bin/apache/apache2.4.41/conf/server.key',
'allow_self_signed' => TRUE,
'verify_peer' => FALSE
)
);
$webServer = new IoServer(
new HttpServer(
new WsServer(
new WebSocketController()
)
),
$webSock
);
$loop->run();
}
注意我把端口号改成了8443(我觉得这没啥关系),也把证书和密钥文件改成了新的,生成如下:
openssl req -config config.conf -new -x509 -out server.crt -days 3650
config.conf 文件是:
[req]
default_bits = 2048
encrypt_key = no
default_md = sha256
default_keyfile = server.key
distinguished_name = req_distinguished_name
prompt = no
[req_distinguished_name]
C = KH
ST = Siem Reap
L = SR
O = AHC
OU = IT
CN = localhost
[bs_section]
CA=false
所有不同之处在于最后一行 CA=false
表明我没有签署或充当证书颁发机构 (CA)。
这摆脱了 MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY
消息。
然后,我删除了在我的 httpd-ssl.conf 文件中定义代理的行:
<VirtualHost *:443>
ServerName ssa
DocumentRoot "d:/web/app/ssa/public"
SSLEngine on
SSLCertificateFile "${SRVROOT}/conf/server.crt"
SSLCertificateKeyFile "${SRVROOT}/conf/server.key"
SSLVerifyClient none
SSLVerifyDepth 10
<Directory "d:/web/app/ssa/public">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require local
</Directory>
#Redirect /wss /wss/
#ProxyPass /wss/ ws://127.0.0.1:8090/
#ProxyPassReverse /ws/ wss://127.0.0.1:8090/
</VirtualHost>
请注意,对于这个虚拟主机,我使用了与安全 Websocket 服务器相同的证书和密钥文件。
好的,这就是我的证书问题。
现在一切正常。