Аутентификация¶
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)
, чтобы убедиться, что пользователь все еще вошел в систему.