Аутентификация

Channels поддерживает стандартную аутентификацию Django из коробки для потребителей HTTP и WebSocket, и вы можете написать собственное промежуточное ПО или код обработки, если вы хотите поддерживать другую схему аутентификации (например, токены в URL).

Аутентификация в Django

AuthMiddleware в Channels поддерживает стандартную аутентификацию Django, где данные пользователя хранятся в сессии. Он позволяет доступ только для чтения к объекту пользователя в scope.

Для работы AuthMiddleware требуется SessionMiddleware, который сам по себе требует CookieMiddleware. Для удобства они также представлены в виде комбинированной вызываемой функции AuthMiddlewareStack, которая включает в себя все три функции.

Чтобы использовать промежуточное программное обеспечение, оберните его вокруг потребителя соответствующего уровня в вашем asgi.py:

from django.urls import re_path

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack

from myapp import consumers

application = ProtocolTypeRouter({

    "websocket": AuthMiddlewareStack(
        URLRouter([
            re_path(r"^front(end)/$", consumers.AsyncChatConsumer.as_asgi()),
        ])
    ),

})

Хотя вы можете обернуть промежуточное ПО вокруг каждого потребителя в отдельности, рекомендуется обернуть его вокруг компонента приложения более высокого уровня, как в данном случае URLRouter.

Обратите внимание, что AuthMiddleware будет работать только с протоколами, которые предоставляют HTTP-заголовки в своих scope - по умолчанию это HTTP и WebSocket.

Чтобы получить доступ к пользователю, просто используйте self.scope["user"] в коде потребителя:

class ChatConsumer(WebsocketConsumer):

    def connect(self, event):
        self.user = self.scope["user"]

Пользовательская аутентификация

Если у вас есть собственная схема аутентификации, вы можете написать собственное промежуточное ПО для разбора деталей и помещения объекта пользователя (или любого другого объекта, который вам нужен) в вашу область видимости.

Middleware написано как вызываемый объект, который принимает ASGI-приложение и оборачивает его, чтобы вернуть другое ASGI-приложение. Большая часть аутентификации может быть выполнена на области видимости, поэтому все, что вам нужно сделать, это переопределить начальный конструктор, который принимает область видимости, а не выполняющуюся по событию coroutine.

Вот простой пример промежуточного ПО, которое просто извлекает идентификатор пользователя из строки запроса и использует его:

from channels.db import database_sync_to_async

@database_sync_to_async
def get_user(user_id):
    try:
        return User.objects.get(id=user_id)
    except User.DoesNotExist:
        return AnonymousUser()

class QueryAuthMiddleware:
    """
    Custom middleware (insecure) that takes user IDs from the query string.
    """

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

    async def __call__(self, scope, receive, send):
        # Look up user from query string (you should also do things like
        # checking if it is a valid user ID, or if scope["user"] is already
        # populated).
        scope['user'] = await get_user(int(scope["query_string"]))

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

Те же принципы можно применить для аутентификации по протоколам, отличным от HTTP; например, вы можете захотеть использовать чье-то имя пользователя из протокола чата, чтобы превратить его в пользователя.

Как войти/выйти из системы

Channels предоставляет прямые функции входа и выхода (подобно тому, как это делает пакет Django contrib.auth) в виде channels.auth.login и channels.auth.logout.

Внутри вашего потребителя вы можете ожидать login(scope, user, backend=None) для входа пользователя в систему. Для этого необходимо, чтобы в вашей области видимости был объект session; лучший способ сделать это - убедиться, что ваш потребитель обернут в SessionMiddlewareStack или << 3 >>>.

Вы можете выйти из пользователя с помощью асинхронной функции logout(scope).

Если вы находитесь в потребителе WebSocket или входите в систему после отправки первого ответа в потребителе http, сессия будет заполнена но не будет сохранена автоматически - вы должны вызвать scope["session"].save() после входа в систему в коде вашего потребителя:

from channels.auth import login

class ChatConsumer(AsyncWebsocketConsumer):

    ...

    async def receive(self, text_data):
        ...
        # login the user to this session.
        await login(self.scope, user)
        # save the session (if the session backend does not access the db you can use `sync_to_async`)
        await database_sync_to_async(self.scope["session"].save)()

При вызове login(scope, user), logout(scope) или get_user(scope) из синхронной функции вам придется обернуть их в async_to_sync, так как мы предоставляем только асинхронные версии:

from asgiref.sync import async_to_sync
from channels.auth import login

class SyncChatConsumer(WebsocketConsumer):

    ...

    def receive(self, text_data):
        ...
        async_to_sync(login)(self.scope, user)
        self.scope["session"].save()

Примечание

Если вы используете долго работающий потребитель, websocket или HTTP с длинным опросом, возможно, что пользователь выйдет из своей сессии в другом месте, пока работает ваш потребитель. Вы можете периодически использовать get_user(scope), чтобы убедиться, что пользователь все еще вошел в систему.

Вернуться на верх