前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HTTPS站点使用WebSocket的错误及解决方案

HTTPS站点使用WebSocket的错误及解决方案

作者头像
joshua317
发布2021-09-08 15:32:47
10.1K0
发布2021-09-08 15:32:47
举报
文章被收录于专栏:技术博文

HTTPS站点使用WebSocket的错误及解决方案

1.在https下使用ws,提示不安全

第一个问题:在https站点下,使用ws://im.joshua317.com 报错,报错信息如下:

代码语言:javascript
复制
Mixed Content: The page at 'https://www.joshua317.com/1.html' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://im.joshua317.com/'. This request has been blocked; this endpoint must be available over WSS.

Uncaught DOMException: Failed to construct 'WebSocket': An insecure WebSocket connection may not be initiated from a page loaded over HTTPS.

我们知道,WebSocket 协议提供了一种创建支持客户端和服务端实时双向通信Web应用程序的方法。作为HTML5规范的一部分,WebSockets简化了开发Web实时通信程序的难度。目前主流的浏览器都支持WebSockets,包括火狐、IE、Chrome、Safari以及Opera等,而且,越来越多的服务器应用框架也开始支持WebSockets。

要在企业产品中使用WebSockets,为满足高性能和高可用性,需要多个WebSocket服务器。负载均衡层需要支持WebSocket协议。Nginx从1.3版起就开始支持WebSocket协议,而且可以担当WebSocket应用程序的反向代理以及实现负载均衡。

WebSocket协议和HTTP协议不同,但是WebSocket协议的握手和HTTP是兼容的,它使用HTTP的Upgrade协议头将连接从HTTP连接升级到WebSocket连接。这个特性使得WebSocket应用程序可以很容易地应用到现有的基础设施。例如,WebSocket应用可以使用标准的80和443 HTTP端口,因此可以通过现有的防火墙设施。

WebSockets应用程序会在客户端和服务器之间建立一个长连接,使得开发实时应用很容易。HTTP的Upgrade协议头机制用于将连接从HTTP连接升级到WebSocket连接,Upgrade机制使用了Upgrade协议头和Connection协议头。反向代理服务器在支持WebSocket协议方面面临着一些挑战。挑战之一是WebSocket是一个逐段转发(hop-by-hop)协议,因此当代理服务器拦截到来自客户端的Upgrade请求时,代理服务器需要将自己的Upgrade请求发送给后端服务器,包括适合的请求头。而且,由于WebSocket连接是长连接,与传统的HTTP端连接截然不同,故反向代理服务器还需要允许这些连接处于打开(Open)状态,而不能因为其空闲就关闭了连接。

Nginx通过在客户端和后端服务器之间建立隧道来支持WebSockets通信。为了让Nginx可以将来自客户端的Upgrade请求发送到后端服务器,Upgrade和Connection的头信息必须被显式的设置。

im.joshua317.com之前已经做过nginx代理,nginx配制如下:

代码语言:javascript
复制
upstream  im-app {
    server 127.0.0.1:9502 max_fails=3 fail_timeout=10s; 
}

server {
    listen 80;
    server_name im.joshua317.com;
    charset utf-8;
    client_max_body_size 50m;
    proxy_hide_header Server;
    proxy_hide_header X-Powered-By;
    add_header X-XSS-Protection 1;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    access_log   /data/log/nginx/wwwroot/im access;
    error_log    /data/log/nginx/wwwroot/im.error;
    location / {
        proxy_pass              http://im-app;
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        proxy_set_header        Whatis-Scheme $scheme;
        #下面三行是重点
        proxy_http_version      1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_connect_timeout  5s;
        proxy_read_timeout  300s;
        proxy_send_timeout  300s;
    }
}

但是为什么会报上面的错误呢,其主要原因如下:

因为HTTPS是基于SSL依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密,所以在HTTPS站点调用某些非SSL验证的资源时浏览器可能会阻止。比如使用ws://调用websocket服务器或者引入类似http://*.js的js文件等都会报错,当使用ws://连接websocket服务器时会出现类似如上的错误。

所以要解决上述的问题,需要使nginx代理支持ssl才可以。具体配置如下:

代码语言:javascript
复制
upstream  im-app {
    server 127.0.0.1:9502 max_fails=3 fail_timeout=10s; 
}

server {
    listen 80;
    server_name im.joshua317.com;
    #调整成自己的证书即可,重点重点重点
    ssl_certificate /usr/local/nginx/conf/ssl/xxxx.crt;
    ssl_certificate_key /usr/local/nginx/conf/ssl/xxxx.key;
    ssl_session_timeout 5m;
     #调整成自己的即可,重点重点重点
    ssl_ciphers xxxxxxxxxxxxx;
    ssl_prefer_server_ciphers on;
    charset utf-8;
    client_max_body_size 50m;
    proxy_hide_header Server;
    proxy_hide_header X-Powered-By;
    add_header X-XSS-Protection 1;
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options SAMEORIGIN;
    access_log   /data/log/nginx/wwwroot/im access;
    error_log    /data/log/nginx/wwwroot/im.error;
    location / {
        proxy_pass              http://im-app;
        proxy_set_header        Host $host;
        proxy_set_header        X-Real-IP $remote_addr;
        proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header        Host $http_host;
        proxy_set_header        Whatis-Scheme $scheme;
        #下面三行是重点
        proxy_http_version      1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_connect_timeout  5s;
        proxy_read_timeout  300s;
        proxy_send_timeout  300s;
    }
}

2.在不支持ssl的情况下,直接用wss链接,提示failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID

场景:

假设我们没有做ssl的处理,直接在链接的时候加上wss://im.joshua317.com,也会保错,报错信息如下:

代码语言:javascript
复制
WebSocket connection to 'wss://im.joshua317.com/' failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID

所以我们需要加上ssl证书才可以,按照上述配置解决。

3.在站点支持https的情况下,但是缺没有websocket情况下链接站点,会出现failed: Error during WebSocket handshake: Unexpected response code: 200

场景:

假设我们的主站是https://www.joshua317.com/, 如果我们直接通过wss://www.joshua317.com会报如下错误

代码语言:javascript
复制
var websocket = new WebSocket("wss://www.joshua317.com");
代码语言:javascript
复制
WebSocket connection to 'wss://www.joshua317.com/' failed: Error during WebSocket handshake: Unexpected response code: 200

当前问题下,nginx配置如下:

代码语言:javascript
复制
server {
    listen 443 ssl;
    server_name www.joshua317.com;
    #调整成自己的证书即可,重点重点重点
    ssl_certificate /usr/local/nginx/conf/ssl/xxxx.crt;
    ssl_certificate_key /usr/local/nginx/conf/ssl/xxxx.com.key;
    ssl_session_timeout 5m;
     #调整成自己的即可,重点重点重点
    ssl_ciphers xxxxxxxxxxxxx;
    ssl_prefer_server_ciphers on;
    charset utf-8;
    proxy_hide_header Server;
    proxy_hide_header X-Powered-By;
    add_header X-XSS-Protection 1;
    add_header X-Content-Type-Options nosniff;
    access_log   /data/log/nginx/wwwroot/blog  access;
    error_log    /data/log/nginx/wwwroot/blog.error;
    root         /data/wwwroot/blog/public/;
    index        index.php index.html index.htm;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|txt)$ {
        expires      3d;
        access_log off;
    }
    location ~ .*\.(js|css)?$ {
        expires      1d;
        access_log off;
    }
    location ~ /\. {
        access_log off;
        deny all;
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location /index.php {
        fastcgi_pass   127.0.0.1:9000;
        include        fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    }

    location = /index.html {
        add_header Cache-Control "no-cache, no-store";
    }
    error_page 404 /404.html;
}

如何解决呢?

此时我们可以通过nginx的location配置以及反向代理,进行处理

代码语言:javascript
复制
upstream  im-app {
    server 127.0.0.1:9502 max_fails=3 fail_timeout=10s; 
}
server {
    listen 443 ssl;
    server_name www.joshua317.com;
    #调整成自己的证书即可,重点重点重点
    ssl_certificate /usr/local/nginx/conf/ssl/xxxx.crt;
    ssl_certificate_key /usr/local/nginx/conf/ssl/xxxx.com.key;
    ssl_session_timeout 5m;
    #调整成自己的即可,重点重点重点
    ssl_ciphers xxxxxxxxxxxxx;
    ssl_prefer_server_ciphers on;
    charset utf-8;
    proxy_hide_header Server;
    proxy_hide_header X-Powered-By;
    add_header X-XSS-Protection 1;
    add_header X-Content-Type-Options nosniff;
    access_log   /data/log/nginx/wwwroot/blog  access;
    error_log    /data/log/nginx/wwwroot/blog.error;
    root         /data/wwwroot/blog/public/;
    index        index.php index.html index.htm;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    #通过location进行处理,重点重点重点
    location /websocket/ {
      proxy_pass http://im-app;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "upgrade";
    }
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|txt)$ {
        expires      3d;
        access_log off;
    }
    location ~ .*\.(js|css)?$ {
        expires      1d;
        access_log off;
    }
    location ~ /\. {
        access_log off;
        deny all;
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:9000;
        fastcgi_index  index.php;
        include        fastcgi_params;
        fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
    location /index.php {
        fastcgi_pass   127.0.0.1:9000;
        include        fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/index.php;
    }

    location = /index.html {
        add_header Cache-Control "no-cache, no-store";
    }
    error_page 404 /404.html;
}

此时,我们链接的方式就是

代码语言:javascript
复制
var websocket = new WebSocket("wss://www.joshua317.com/websocket/");

4.如果我们设置location不正确的时候,会报错误:failed: Error during WebSocket handshake: Unexpected response code: 404

出现这个问题主要就是在做location的配置的时候出了差错,比如我多加了个s,和配置里面有出入。

所以一定要严格按照location规则进行处理。

客户端websocket的代码如下,仅供参考:

代码语言:javascript
复制
<script>
//判断当前浏览器是否支持WebSocket
    if ('WebSocket' in window) {
        websocket = new WebSocket("wss://www.joshua317.com/websocket");

        websocket.onopen = function (ev) {
            console.log("open");
            websocket.send("hello");
        };

        //客户端收到服务器的方法,这个方法就会被回调
        websocket.onmessage = function (ev) {
            console.log("接收到消息:"+ev.data);
        };

        websocket.onclose = function (ev) {
            console.log("关闭连接");
        };
        websocket.onerror = function (ev) {
            console.log("连接错误");
        };

    } else {
        alert('当前浏览器 Not support websocket')
    }
</script>

本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/61

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-02 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • HTTPS站点使用WebSocket的错误及解决方案
    • 1.在https下使用ws,提示不安全
      • 2.在不支持ssl的情况下,直接用wss链接,提示failed: Error in connection establishment: net::ERR_CERT_COMMON_NAME_INVALID
        • 3.在站点支持https的情况下,但是缺没有websocket情况下链接站点,会出现failed: Error during WebSocket handshake: Unexpected response code: 200
          • 4.如果我们设置location不正确的时候,会报错误:failed: Error during WebSocket handshake: Unexpected response code: 404
          相关产品与服务
          SSL 证书
          腾讯云 SSL 证书(SSL Certificates)为您提供 SSL 证书的申请、管理、部署等服务,为您提供一站式 HTTPS 解决方案。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档