первые шаги в Python
April 2, 2021

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

https://channels.readthedocs.io/en/latest/topics/authentication.html#authentication

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

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

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

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

Чтобы использовать промежуточное ПО, оберните его вокруг соответствующего уровня потребителя (consumer) в 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)/quot;, 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"]

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

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

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

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

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:
    """
    Пользовательское промежуточное ПО (небезопасное), 
    которое берет идентификаторы пользователей из строки запроса.
    """

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

    async def __call__(self, scope, receive, send):
    
        # Найдите пользователя в строке запроса (вы также должны сделать 
        # такие вещи, как проверка, является ли он действительным 
        # идентификатором пользователя или уже заполнена 
        # область ["пользователь"]).
        
        scope['user'] = await get_user(int(scope["query_string"]))

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

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

Как выполнить вход / выход пользователя

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

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

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

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

from channels.auth import login

class ChatConsumer(AsyncWebsocketConsumer):

    ...

    async def receive(self, text_data):
        ...
        # вход пользователя в эту сессию.
        await login(self.scope, user)
        # сохранить сеанс (если серверная часть сеанса не имеет доступа к базе данных, вы можете использовать `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()

Примечание

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