在使用令牌身份验证时,如何在django通道中的websocket连接中对用户进行身份验证

在使用令牌身份验证时,如何在django通道中的websocket连接中对用户进行身份验证,django,rest,django-rest-framework,token,django-channels,Django,Rest,Django Rest Framework,Token,Django Channels,我在项目中使用前端框架()和RESTAPI。另外,对于JSON web令牌身份验证,我正在使用。成功登录后,将向用户提供令牌。此令牌被传递到每个请求中,以获取任何与API相关的内容 现在我想整合到我的项目中。因此,在成功登录之后,当客户端接收到令牌时,我想启动websocket连接。然后在服务器(消费者)上,我想检查请求的用户是否是匿名的。如果请求的用户是匿名的,我想关闭连接或接受它 这就是我到目前为止所做的: 客户端: const socket = new WebSocket("ws://"

我在项目中使用前端框架()和RESTAPI。另外,对于JSON web令牌身份验证,我正在使用。成功登录后,将向用户提供令牌。此令牌被传递到每个请求中,以获取任何与API相关的内容

现在我想整合到我的项目中。因此,在成功登录之后,当客户端接收到令牌时,我想启动websocket连接。然后在服务器(消费者)上,我想检查请求的用户是否是匿名的。如果请求的用户是匿名的,我想关闭连接或接受它

这就是我到目前为止所做的:

客户端:

const socket = new WebSocket("ws://" + "dev.site.com"+ "/chat/");
routing.py:

channel_routing = [
    route("websocket.connect", ws_connect),
    ...
    ...
]
消费者:

def ws_connect(message):

    # if the user is no anonymous
    message.reply_channel.send({
        "accept": True
    })

    # else
    message.reply_channel.send({
        "close": True
    })

在中有一个来自http的decorator
@channel\u session\u user\u,它将提供一条
消息。user
。但我使用的是令牌而不是会话。在使用令牌身份验证时,如何检查连接上的用户,以便接受或关闭连接。或者,如果有更好的方法,你能给我一个建议吗。谢谢。

问题是浏览器不支持在websocket升级时传递jwt auth头,所以基本上就是这样。不久前我遇到了这个问题,并提出了通过查询参数传递令牌的解决方案-注意如果没有TLS,这是完全不安全的,因为您在URI中公开了身份验证。我再也无法获得确切的代码,但我的想法是:

from channels.generic.websockets import JsonWebsocketConsumer
from channels.handler import AsgiRequest

from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from jwt.exceptions import InvalidTokenError
from rest_framework.exceptions import ValidationError

class Consumer(JsonWebsocketConsumer):

    def connect(self, message, **kwargs):
        # construct a fake http-like request object from the message
        message.content.setdefault('method', 'FAKE')
        request = AsgiRequest(message)
        # validate the token
        try:
            VerifyJSONWebTokenSerializer().validate(request.GET)
            super().connect(message, **kwargs)
        except (KeyError, InvalidTokenError, ValidationError,):
            # token is either not available or invalid
            # so we disconnect the user
            message.reply_channel.send({'close': True})
将消费者注册到

channel_routing = [
    ...
    route_class(Consumer, path=r'^my-ws-endpoint$'),
]
在浏览器端,您可以通过在websocket URI中将令牌作为查询参数传递来建立websocket连接:

let-token:string='my-token';//拿到代币
让wsHandler:$WebSocket=new$WebSocket('wss://example.com/my-ws-endpoint/?token=“+代币,…);
然后,您可以在类似于http中的
@channel\u session\u user\u的decorator中提取auth check代码,只需修饰连接例程,或者在使用基于类的路由时将代码提取到mixin

我想重申,如果不使用加密,这种方法是完全不安全的,因此在生产中,URI应该从
https/wss
开始



编辑:这是一个用于DRF令牌身份验证的方法,适用于基于函数和基于类的路由。它与我的方法几乎相同,构造一个请求对象并将其传递给身份验证器。

@hoefling的答案是我的指南。我对认证用户的两件事感到困惑

  • 这个代币怎么办

    • 您可以将令牌作为查询字符串传递并获取该查询参数。了解有关如何获取查询参数的更多信息
    • 或者,如果您已经在请求的授权头中传递了它,那么您可以从那里获得它,就像@hoefling对他的回答所做的那样。记住先假装那个请求
  • 如何验证该令牌并获取用户

    • 最后,
      VerifyJSONWebTokenSerializer
      类是验证令牌并获取该令牌的用户对象所需的全部。(谢谢@hoefling!)您可以阅读django rest framework jwt的实际代码
  • 所以,我最终是这样做的:

    def ws_connect(message):
        message.content.setdefault('method', 'FAKE')
        django_request = AsgiRequest(message)
        token = django_request.GET['token'].split(' ')[1]
        try:
            data = {'token': token}
            valid_data = VerifyJSONWebTokenSerializer().validate(data)
            user = valid_data['user']
            ...
            ...
            message.reply_channel.send({
                "accept": True
            })
        except (KeyError, InvalidTokenError, ValidationError,):
            ...
            ...
            message.reply_channel.send({
                "text": "Authentication error",
                "close": True
            })
    

    伟大的你的回答对我帮助很大!谢谢。很高兴我能帮忙!https/wss expose thr URI?@injaon您的身份验证令牌在未加密的情况下公开,例如在发生MITM攻击的情况下。