Django DRF websocket 通道 2 并使用来自 Simple JWT 的令牌进行授权

Posted

技术标签:

【中文标题】Django DRF websocket 通道 2 并使用来自 Simple JWT 的令牌进行授权【英文标题】:Django DRF websocket channels 2 and authorize with token from Simple JWT 【发布时间】:2020-10-23 14:35:18 【问题描述】:

我有一个简单的聊天,我使用渠道 2。在用户连接到消费者之前,我在通过令牌 JWT(简单 JWT)授权时遇到问题。

我的中间件:

from channels.auth import AuthMiddlewareStack
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser


class TokenAuthMiddleware:
    """
    Token authorization middleware for Django Channels 2
    """

    def __init__(self, inner):
        self.inner = inner

    def __call__(self, scope):
        headers = dict(scope['headers'])
        if b'authorization' in headers:
            try:
                token_name, token_key = headers[b'authorization'].decode().split()
                if token_name == 'Token':
                    token = Token.objects.get(key=token_key)
                    scope['user'] = token.user
            except Token.DoesNotExist:
                scope['user'] = AnonymousUser()
        return self.inner(scope)

TokenAuthMiddlewareStack = lambda inner: TokenAuthMiddleware(AuthMiddlewareStack(inner))

来自我的消费者的片段:

User = get_user_model()


class ChatConsumer(WebsocketConsumer):

    def connect(self):
        """
        Join channel group by chatname.
        """
        self.group_name = 'chat_0'.format(self.scope['url_route']['kwargs']['chatname'])

        if self.scope['user'] == AnonymousUser():
            raise DenyConnection("Invalid User")

        async_to_sync(self.channel_layer.group_add)(
            self.group_name,
            self.channel_name,
        )

        self.accept()

    def disconnect(self, close_code):
        """
        Leave channel by group name.
        """
        async_to_sync(self.channel_layer.group_discard)(
            self.group_name,
            self.channel_name
        )

我的路线:

from .auth import TokenAuthMiddlewareStack

application = ProtocolTypeRouter(
    'websocket': TokenAuthMiddlewareStack(
        URLRouter([
            path('ws/chat/<slug:chatname>/', ChatConsumer),
        ])
    ),
)

现在有两个问题。

如何在我的测试客户端 Web 套接字中传递令牌? 上面的 sn-p 是正确的吗?因为我从客户那里收到

    raise InvalidStatusCode(status_code)
websockets.exceptions.InvalidStatusCode: server rejected WebSocket connection: HTTP 403

我的测试客户:

from asgiref.sync import async_to_sync
import websockets
import asyncio
import json
test_json = "some test to send via socket"
def test_url2(url):
    async def inner():
        async with websockets.connect(url) as websocket:
            await websocket.send(test_json)
            print(test_json)
    return asyncio.get_event_loop().run_until_complete(inner())

test_url2("ws://0.0.0.0:8080/ws/over/3ff621b1-b392-4a82-ab35-a0ad81ab1179_f99c82ba-1404-442c-ba08-653f84d58aa4")

感谢回复

【问题讨论】:

【参考方案1】:

我找不到这样做的方法,作为一种解决方法,我从请求对象访问了用户。设置将为您提供请求对象的中间件:

#get_request.py
from threading import current_thread

from django.utils.deprecation import MiddlewareMixin


_requests = 


def current_request():
    return _requests.get(current_thread().ident, None)


class RequestMiddleware(MiddlewareMixin):

    def process_request(self, request):
        _requests[current_thread().ident] = request

    def process_response(self, request, response):
        # when response is ready, request should be flushed
        _requests.pop(current_thread().ident, None)
        return response

    def process_exception(self, request, exception):
        # if an exception has happened, request should be flushed too
        _requests.pop(current_thread().ident, None)

在你的 settings.py 中添加到现有的 MIDDLEWARE 列表中:

MIDDLEWARE = [
   ... 
    'users.get_request.RequestMiddleware',
   ...
]

最后得到用户:(在你的consumers.py或任何地方)

from users.get_request import current_request

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.user = current_request().user

【讨论】:

以上是关于Django DRF websocket 通道 2 并使用来自 Simple JWT 的令牌进行授权的主要内容,如果未能解决你的问题,请参考以下文章

Ubuntu + Django(DRF) + channels(websocket)+NGINX + uwsgi 环境部署

Django rest 框架,Django 通道,Ionic2 - websocket 握手错误

如何通过 Django 通道 WebSocket 传递请求并调用 Django 视图

如何使用 Django 通道为 Django Rest Api 打开 Websocket?

如何在 django 通道上使用令牌认证来认证 websocket?

websocket启动时的django通道错误消息(Sessions.py)