Websockets 到 localhost 在基于 chromium 的浏览器中失败

Posted

技术标签:

【中文标题】Websockets 到 localhost 在基于 chromium 的浏览器中失败【英文标题】:Websockets to localhost fails in chromium based browsers 【发布时间】:2022-01-11 22:23:00 【问题描述】:

我正在尝试构建一个使用vue.js 作为前端和django 作为后端的聊天网站。它在Firefox 中运行良好,但在MS edgeGoogle Chrome 中,Websocket 失败了。我在浏览器控制台中收到此消息。

WebSocket connection to 'ws://127.0.0.1:8000/inbox/' failed

我使用django-channels,所以它在 python 控制台中打印

WebSocket CONNECT /inbox/ [127.0.0.1:4965]
WebSocket DISCONNECT /inbox/ [127.0.0.1:4965]

当我打印出错误代码时,我得到1006

关闭代码 1006 是一个特殊代码,表示连接已关闭 由浏览器实现异常(本地)。

我的 WebSocket 代码

new WebSocket(url, authToken) // I use sec-websocket-protocol to transfer the authentication token

我做错了什么还是浏览器有问题?

-- 更新

new WebSocket("ws://127.0.0.1:8000/inbox/", "authtoken");

所以,我在第二个 Websocket 协议中发送身份验证令牌,并使用中间件在后端对用户进行身份验证。 当我删除该协议并在后端接受未经身份验证的用户时->

new WebSocket("ws://127.0.0.1:8000/inbox/");

-> WebSocket 连接得很好。问题是在发送第二个 Websocket 协议时。

【问题讨论】:

您使用的是哪个版本的操作系统、Edge 和 Chrome?能否请您使用WebSocket.onerror 检查错误详情?根据this answer,代码 1006 是 WebSocket 本身的低级错误,它在您的代码和实现中。 我的操作系统是Windows 10 Pro OS build 19042.1288,Edge 浏览器版本是96.0.1054.43(最新)。我尝试使用WebSocket.onerror 打印错误,但它没有提供太多信息。 @玉舟 我使用NodeJS WebSocket server ws 做了一个简单的示例,它在带有本地主机的 Edge 和 Chrome 中运行良好。我认为这个问题可能与 django-channels 有关。您可以参考this thread 看看是否有帮助。此外,您的域使用http 还是https @YuZhou 感谢您的样品。我的域使用http 如果您使用的是简单的 NodeJS WebSocket 示例,它可以在 Edge 和 Chrome 中使用吗? localhost 域在 Edge 和 Chrome 中运行良好吗?如果没有可重复的样本,很难找到问题所在。如果可能,您可以提供可以重现问题的步骤和最小代码 sn-p。 【参考方案1】:

我将“令牌”作为子协议添加到 WebSocket 和 authToken 中。然后在后端,我用相同的子协议名称接受它:

我的网络套接字

new WebSocket(url, ["Token", authToken]) // Sending 'Token' and authToken as subprotocols

令牌认证中间件

from asgiref.sync import sync_to_async
from django.contrib.auth.models import AnonymousUser

from rest_framework.authtoken.models import Token


@sync_to_async
def get_user(headers):
    try:
        token_key = headers[b"sec-websocket-protocol"].decode().split(', ')[1]
        token = Token.objects.get(key=token_key)

        return token.user
    except Token.DoesNotExist:
        return AnonymousUser()


class TokenAuthMiddleware:
    def __init__(self, app):
        self.app = app

    async def __call__(self, scope, receive, send):
        headers = dict(scope["headers"])

        if b"sec-websocket-protocol" in headers:
            scope['user'] = await get_user(headers)

        return await self.app(scope, receive, send)

Consumers.py

from channels.generic.websocket import AsyncWebsocketConsumer

class Consumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = self.scope['user']
        if self.user.is_authenticated:
            self.room_name = await self.get_user_id(self.user)
            self.room_group_name = self.room_name

            await self.channel_layer.group_add(self.room_group_name, self.channel_name)
            await self.accept('Token')  # Here I'm giving a subprotocol to the self.accept function

        else:
            await self.close()

【讨论】:

以上是关于Websockets 到 localhost 在基于 chromium 的浏览器中失败的主要内容,如果未能解决你的问题,请参考以下文章

如何在共享主机上部署 laravel websockets?

ASP.NET MVC 4 和 WebSockets

WebSockets 握手后 Chrome 断开连接

ReactJS 使用 WebSockets 与服务器通信

如何通过 websockets 在本地 RSK 节点上订阅“newBlockHeaders”?

Websocket 在 localhost 但不是 Heroku