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)чтобы быть уверенным, что пользователь все еще залогинен в системе.