HTTPS站點使用WebSocket的錯誤及解決方案


HTTPS站點使用WebSocket的錯誤及解決方案

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

第一個問題:在https站點下,使用ws://im.joshua317.com 報錯,報錯信息如下:

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配制如下:

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才可以。具體配置如下:

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,也會保錯,報錯信息如下:

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會報如下錯誤

var websocket = new WebSocket("wss://www.joshua317.com");
WebSocket connection to 'wss://www.joshua317.com/' failed: Error during WebSocket handshake: Unexpected response code: 200

當前問題下,nginx配置如下:

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配置以及反向代理,進行處理

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

此時,我們鏈接的方式就是

var websocket = new WebSocket("wss://www.joshua317.com/websocket/");

 

 

 

4.如果我們設置location不正確的時候,會報錯誤:failed: Error during WebSocket handshake: Unexpected response code: 404

出現這個問題主要就是在做location的配置的時候出了差錯,比如我多加了個s,和配置里面有出入。

 

 

 

所以一定要嚴格按照location規則進行處理。

 

 

 

 

 

 

客戶端websocket的代碼如下,僅供參考:

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM