Django频道2个websockets多个AuthMiddlewareStacks

时间:2018-06-15 22:15:53

标签: python django django-rest-framework django-channels

尝试使用带有django频道的websockets创建一些应用。 我有2个websocket客户端 - 一个是web界面/ js app,另一个是python应用程序。我想为他们(他们的消费者)提供不同的授权要求(最好是使用AuthMiddlewareStacks的方式)

我该如何实现?无法在文档https://channels.readthedocs.io/en/latest/topics/routing.html

中找到答案

这是一个' sketch'。 (routing.py不会以这种方式工作)。 我使用DRF,DRF-JWT,django频道2,如果它是相关的。

appmain.routing.py

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(
            app.routing.websocket_cli_urlpatterns
        )
    ),
    "websocket_0": TokenAuthMiddlewareStack(
        URLRouter([
            app.routing.websocket_web_urlpatterns
        ]),
    )
})

app.routing.py

websocket_cli_urlpatterns = [
    path('ws/app/<str:var1>/<str:var2>/', consumers.CliConsumer),
]

websocket_web_urlpatterns = [
    path('ws/app/<str:var1>/', consumers.WebConsumer),
]

谢谢!

3 个答案:

答案 0 :(得分:0)

编写您自己的身份验证处理程序,该处理程序结合了要使用的身份验证处理程序中的逻辑。 (您可以只调用它们的 call 方法,并且只有在两个都失败时才失败。)从GET参数或http标头中处理令牌,以便URL相同。

答案 1 :(得分:0)

# middleware.py
from urllib.parse import parse_qs

import jwt
from channels.auth import AuthMiddlewareStack
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from django.utils.translation import ugettext as _
from rest_framework import exceptions
from rest_framework_jwt.authentication import jwt_decode_handler, jwt_get_username_from_payload


class JWTAuthMiddleware():
    """
    Token authorization middleware for Django Channels 2
    """

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

    def __call__(self, scope):
        query_dict = {k: v[0] for k, v in parse_qs(scope["query_string"].decode()).items()}

        jwt_value = query_dict.get('token', None)
        if jwt_value:
            try:
                try:
                    payload = jwt_decode_handler(jwt_value)
                except jwt.ExpiredSignature:
                    msg = _('Signature has expired.')
                    raise exceptions.AuthenticationFailed(msg)
                except jwt.DecodeError:
                    msg = _('Error decoding signature.')
                    raise exceptions.AuthenticationFailed(msg)
                except jwt.InvalidTokenError:
                    raise exceptions.AuthenticationFailed()

                scope['user'] = self.authenticate_credentials(payload)

            except exceptions.AuthenticationFailed:
                scope['user'] = AnonymousUser()

        return self.inner(scope)

    def authenticate_credentials(self, payload):
        """
        Returns an active user that matches the payload's user id and email.
        """
        User = get_user_model()
        username = jwt_get_username_from_payload(payload)

        if not username:
            msg = _('Invalid payload.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            user = User.objects.get_by_natural_key(username)
        except User.DoesNotExist:
            msg = _('Invalid signature.')
            raise exceptions.AuthenticationFailed(msg)

        if not user.is_active:
            msg = _('User account is disabled.')
            raise exceptions.AuthenticationFailed(msg)

        close_old_connections()
        return user


JWTAuthMiddlewareStack = lambda inner: JWTAuthMiddleware(AuthMiddlewareStack(inner))



#routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from core.middleware import JWTAuthMiddlewareStack

application = ProtocolTypeRouter({
        'websocket':
            JWTAuthMiddlewareStack(
                URLRouter(apps.myapp.routing.websocket_urlpatterns)
            )

    })

答案 2 :(得分:0)

我做到了,而且奏效了:

# consumers.py

from channels.auth import AuthMiddlewareStack

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

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

    def __call__(self, scope):
        close_old_connections()

        headers = dict(scope['headers'])
        if b'authorization' in headers:
            try:
                token_name, token_key = headers[b'authorization'].decode().split()
                if token_name == 'bearer':
                    printer = Printer.objects.select_related('user').get(auth_token=token_key)
                    scope['user'] = printer
                else:
                    scope['user'] = None
            except ObjectDoesNotExist:
                scope['user'] = None
        return self.inner(scope)

TokenAuthMiddlewareStack = lambda inner: PrinterWSAuthMiddleWare(AuthMiddlewareStack(inner))
# routing.py

...
    'websocket': TokenAuthMiddlewareStack(
        URLRouter(
            api.ws_routing.websocket_urlpatterns
        )
    ),
...

它没有记录,但是AuthMiddlewareStack在后​​台检查Cookie和会话并设置范围(如果其中任何一个有效)。