了解Django渠道-QueryAuthMiddleware

时间:2018-11-07 19:20:12

标签: django authentication channels

如何编写通过ws://协议连接到聊天的用户的自定义身份验证?该用户在Django应用的另一侧,他是移动用户,通过移动应用中的ws://连接websocket。我试图用chrome扩展来测试websocket,但是它无法连接到我的websocket。我认为是因为身份验证。

在Django渠道文档中,它说:

  

如果您有一个自定义的身份验证方案,则可以编写一个自定义的中间件来解析详细信息,并将用户对象(或所需的任何其他对象)放入您的范围内。中间件被编写为可调用的方法,它采用了ASGI应用程序,并且包装它以返回另一个ASGI应用程序。大多数身份验证只能在范围内完成,因此您需要做的就是覆盖采用范围的初始构造函数,而不是运行事件的协程。   这是一个中间件的简单示例,该中间件只是从查询字符串中取出用户ID并使用该ID:   可以将相同的原理应用于通过非HTTP协议进行身份验证;例如,您可能要使用聊天协议中某人的聊天用户名将其转换为用户。

from django.db import close_old_connections

class QueryAuthMiddleware:
    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):
        # Look up user from query string (you should also do things like
        # check it's a valid user ID, or if scope["user"] is already populated)
        user = User.objects.get(id=int(scope["query_string"]))
        close_old_connections()
        # Return the inner application directly and let it run everything else
        return self.inner(dict(scope, user=user))

我需要做什么查询?我对该用户一无所知,实际上是匿名用户。

请帮助我。

2 个答案:

答案 0 :(得分:0)

在此示例代码中,您可能必须使用以下命令打开websocket连接:

ws://SERVER:PORT/PATH?1

?之后的所有内容都是查询字符串。在示例代码中,您的query_string必须是用户ID,例如1

您可以更改代码以使用不同的查询字符串。例如,您可以使用:

from urllib.parse import parse_qs
from django.db import close_old_connections

class QueryAuthMiddleware:
    def __init__(self, inner):
        # Store the ASGI application we were passed
        self.inner = inner

    def __call__(self, scope):
        # Look up user from query string (you should also do things like
        # check it's a valid user ID, or if scope["user"] is already populated)

        query_string = parse_qs(self.scope['query_string'])
        if b'user_id' in query_string:
            user = User.objects.get(id=int(query_string[b'user_id'][0]))
            close_old_connections()
        else:
            user = AnonymousUser
        # Return the inner application directly and let it run everything else
        return self.inner(dict(scope, user=user))

现在,您可以使用此uri了:

ws://SERVER:PORT/PATH?user_id=1

您必须确保具有ID的用户存在于数据库中。您还必须编写实际的身份验证代码。每个用户都可以使用任意用户ID连接到该应用程序。不需要密码或身份验证令牌。

答案 1 :(得分:0)

我也遇到了同样的问题,下面的代码片段对解决方案进行了一些研究:

假设您已定义User类。您想要通过查询对用户进行身份验证,同时与ws建立连接时发送查询。

我将通过渠道安装和配置,假设您已成功安装并配置了渠道。

QueryAuthMiddleware类,如下所示:

from channels.auth import AuthMiddlewareStack
from django.conf import LazySettings
from urllib import parse
from rest_socket.models import User
from urllib.parse import parse_qs
from urllib.parse import unquote, urlparse

from channels.auth import AuthMiddlewareStack

from django.contrib.auth.models import AnonymousUser

class QueryAuthMiddleware:
    """
    QueryAuthMiddleware authorization 
    """


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

    def __call__(self, scope):
        query_string = parse_qs(scope['query_string']) #Used for querystring token url auth
        headers = dict(scope['headers']) #Used for headers token url auth

        print("qeurry", query_string)
        if b'user' in query_string:
            try:
                user = query_string[b'user'][0].decode()
                print("user", user)
                existing = User.objects.filter(id=user).last()
                print(existing)
                if existing:
                    print("existinguser")
                    scope['user'] = existing

                else:
                    scope["user"]  ="no user found" 


            except User.DoesNotExist:
                pass
        return self.inner(scope)


QueryAuthMiddlewareStack = lambda inner: QueryAuthMiddleware(AuthMiddlewareStack(inner))

您的routing.py应该是这样的:

from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import OriginValidator
from django.urls import path
import your_app.routing
from project.utils.auth import QueryAuthMiddlewareStack

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': QueryAuthMiddlewareStack(
        URLRouter(
            your_app.routing.websocket_urlpatterns
        )
    ),
})

routing inside application:

websocket_urlpatterns = [
    path("tasks/", consumers.Task)
]

以及您与jango-channels的ws连接:


<script>



    var notification;
    if (location.protocol === 'https:') {
        notification = new WebSocket('wss://' + "window.location.host" + "/tasks"+ "/?user=id");

        console.log("with htpps")
    }
    else {

        notification = new WebSocket('ws://' + window.location.host +  "/tasks"+ "/?userr=id");


        console.log("htpp")

    }

    notification.onopen = function open() {
        console.log('notification connection created for NotificationWebsocket.');
    };
    notification.onmessage = function message(event) {
        var data = JSON.parse(event.data);
        console.log("Socket response from NotificationWebsocket => ", data);
    };
    if (notification.readyState === WebSocket.OPEN) {
        notification.onopen();
    }


</script>