Аутентификация токена в каналах Django и веб-сокетах

Опубликовано: 19 Февраля, 2023

Предварительные требования: Django, WebSockets, каналы Django, аутентификация по токену.

Самые популярные темы Django сейчас — это WebSockets и каналы Django, потому что они позволяют быстро и легко общаться, работая в режиме реального времени без постоянного обновления страницы. При работе только с механизмом шаблонов Django, а не с инфраструктурой REST, метод аутентификации, который используют каналы Django, — это аутентификация сеанса. Пользователь, который в данный момент вошел в систему, также отображается в системе аутентификации каналов Django.

// This is not possible in websockets .

var socket = new WebSocket(“path_of_the_socket” , headers = { “Authorization’: “token token_string” })

Что ж, использование веб-аутентификации — это один из способов достижения вашей цели. Возврат файла cookie на веб-сайт позволяет вам еще раз подключиться к серверу WebSocket с файлом cookie, пройти аутентификацию и затем продолжить. Однако аутентификация на основе файлов cookie может быть крайне ненадежной и иметь различные проблемы. Поэтому, чтобы решить эту проблему, мы можем передать токен аутентификации из параметров запроса сокета, а затем создать новый стек аутентификации, который будет располагаться поверх существующих при маршрутизации соединений WebSocket.

Однако работа с остальным фреймворком с аутентификацией по токену или аутентификацией JWT непроста для веб-сокетов и каналов; в результате может быть сложно войти в систему пользователя через каналы и сокеты при использовании аутентификации с помощью токена или остальной инфраструктуры. Вы не можете отправить токены аутентификации/JWT в заголовки WebSocket, как в простом сообщении, или получить запрос в заголовке авторизации.

Примечание. Этот процесс является полностью асинхронным, поскольку веб-сокеты и каналы работают с ASGI (асинхронный интерфейс шлюза сервера).

Настройка нашего проекта

Структура файла

Это файловая структура после выполнения всех шагов, описанных в этой статье.

Шаг 1: Создайте виртуальную среду и активируйте ее.

Шаг 2: Установите Django и запустите свой проект.

Шаг 3. Создайте проект с именем TokenAuth.

django-adminb startproject TokenAuth

Шаг 4: Запустите приложение с именем token_auth_app, перейдя в папку проекта.

cd TokenAuth
python manage.py startapp token_auth_app

Шаг 5: Добавьте свое приложение в список установленных приложений в settings.py.

"token_auth_app.apps.TokenAuthAppConfig"

Шаг 6: Настроить каналы Django для серверной части. Установить Django-каналы

python -m pip install -U channels

Шаг 7: Добавьте каналы в установленные приложения.

Python3




INSTALLED_APPS = [
    "token_auth_app.apps.TokenAuthAppConfig",
    "channels"
]

Шаг 8. Для создания аутентификации по токену для серверных приложений требуется установка фреймворка REST. Для аутентификации токенов используется пакет токенов аутентификации остальной платформы, который содержит модель токена.

pip install django-rest-framework

Шаг 9: Установите корр.

pip install django-cors-headers

Шаг 10: Добавьте как в INSTALLED_APPS, так и в пакет аутентификации токена.

Python3




INSTALLED_APPS = [
    "token_auth_app.apps.TokenAuthAppConfig",
    ...
    "channels",
    "rest_framework",
    "rest_framework.authtoken",
    "corsheaders",
]

Шаг 11: Установите для параметра ALLOWED_HOSTS значение *. Кроме того, добавьте промежуточное ПО cors в список MIDDLEWARE. Настройка cors выполняется таким образом, чтобы хост разрешал все источники, и мы могли взаимодействовать с серверным приложением.

Python3




ALLOWED_HOSTS = ["*"]
  
CORS_ALLOW_ALL_ORIGINS = True
  
  
MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
  
    # -----------------------------------------
    "corsheaders.middleware.CorsMiddleware",
    # -----------------------------------------
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

Реализация кода

Настройка файлов по одному:

файл token_auth_app/consumers.py:

В папке приложения создайте Consumer.py, который будет обрабатывать все входящие команды приема и отправки из WebSocket.

Python3




from channels.generic.websocket import AsyncJsonWebsocketConsumer
  
class TokenAuthConsumer(AsyncJsonWebsocketConsumer):
    async def connect(self):
        await self.accept()
        print(self.scope["user"].username)
        print(self.scope["user"].email)
  
    async def disconnect(self, close_code):
        ...
  
    async def receive_json(self, message):
        command = message.get("command")
        if command == "Say hello !":
            print(message["data_string"])
            await self.send_json({
                "command_response": "The command to
                say hello was recevied ",
                "data_string_bacK": message.get
              ("data_string", None)
            })

Объяснение кода:

Мы создали Consumer с именем класса TokenAuthConcumser, так как он использует события, отправленные и полученные приложением ASGI, создание которого мы увидим позже. Здесь мы наследуем AsyncJsonWebsocketConsumer , который является родительским классом-потребителем. В модуле AsyncJsonWebsocketConsumer у нас есть функции send_json и Receive_json, которые принимают JSON и отправляют JSON без дампа JSON.

  1. функция подключения: первая и основная функция, которая выполняется для установления соединения, — это та, которая принимает запрос WS на подключение, поэтому мы должны подключить его. Область действия — это словарь, который работает аналогично параметру запроса в функциональных представлениях (def fun(request)) в том смысле, что он содержит всю информацию о входящем соединении. доступ к пользователю осуществляется в виде области объекта dict["user"], которая предоставляет информацию о пользователе, который в данный момент вошел в систему. Затем извлекаются и отображаются имя пользователя и адрес электронной почты пользователя.
  2. функция отключения: принимает параметр close_code, а затем ничего не делает.
  3. Функция receive_json: мы должны реализовать метод получения в AsyncWebsocketConsumer. затем, чтобы получить элементы, мы должны загрузить данные (json.loads). Тем не менее, у вас есть прямой доступ к материалам здесь. Эта процедура использует сообщение, отправленное с внешнего интерфейса. Мы отправим эти данные, как указано ниже:

Javascript




{
    "command" : "Say hello !"
    "data_string" : "This is the data string !"
}

По мере получения этих данных мы интерпретируем их и соответствующим образом отправляем ответы во внешнем интерфейсе.

файл token_auth_app/middlewares.py:

У него будет пользовательская аутентификация, которую мы создадим, и пользовательский стек будет наложен поверх всех стеков.

Python3




from rest_framework.authtoken.models import Token
from urllib.parse import parse_qs
from channels.db import database_sync_to_async
from django.contrib.auth.models import AnonymousUser
  
  
@database_sync_to_async
def returnUser(token_string):
    try:
        user = Token.objects.get
            (key=token_string).user
    except:
        user = AnonymousUser()
    return user
  
  
class TokenAuthMiddleWare:
    def __init__(self, app):
        self.app = app
  
    async def __call__(self, scope, receive, send):
        query_string = scope["query_string"]
        query_params = query_string.decode()
        query_dict = parse_qs(query_params)
        token = query_dict["token"][0]
        user = await returnUser(token)
        scope["user"] = user
        return await self.app(scope, receive, send)

Объяснение кода:

Использование различных модулей описано ниже:

  1. Токен: используется для проверки подлинности токена.
  2. parse_qs: используется для разбора параметров запроса из строки в словарь.
  3. database_sync_to_async: используется для извлечения данных из базы данных, поскольку Django ORM полностью синхронен.
  4. AnonymousUser: Это помогает, если мы не находим пользователя на основе токена, т.е. функция returnUser возвращает пользователя, связанного с токеном, переданным функции, иначе она возвращает AnonymousUser.

В функции __init__ мы определяем текущий экземпляр приложения для приложения, которое передается в стек. Затем асинхронная функция __call__() принимает три параметра: область приема и отправки. Область действия — это словарь, в котором есть все данные о соединении, как указано выше. send и receive — это команды для отправки и получения команд. Каждое приложение ASGI имеет три параметра: окружение, отправка и получение, здесь окружение — это переменная области видимости. query_string — это строка, переданная из предварительного запроса, например:

ws://localhost:8000/?token=fkdsahjkfshiadjkfhoasdfk"

Строка запроса, указанная в переменной строки запроса в области действия, имеет вид token=fkdsahjkfshiadjkfhoasdfk. Он отправляется в байтах в виде строки. Это указывает на то, что тип строки запроса — байты.
Строка байтов преобразуется в строку Python с помощью запроса string.decode(). p Parse qs(параметры запроса), с другой стороны, декодирует строку и добавляет ее в словарь Python. Диктовка представлена в виде пары ключ-значение. Поэтому мы получаем доступ к ключу токена, затем к первому элементу токена, token=qury_dict["token"][0], и, наконец, мы посещаем метод returnUser для получения пользователя. и мы устанавливаем его в свойство «user» области перед возвратом приложения.

Интересная часть этой переменной области видимости заключается в том, что вы можете полностью изменить ее и внести любые изменения, которые пожелаете. Это означает, что вы можете свободно изменять переменные в параметре области действия, а также добавлять новые.

для добавления просто добавьте как обычный словарь Python, т.е. scope["some_another_key"] = "value_for_the_key",

для удаления, del scope["some_key_that_exist"]

Файл TokenAuth/routing.py:

Он создаст приложение ASGI, которое будет обслуживаться и направлять все асинхронные URL-адреса WebSocket соответствующим потребителям.

Python3




from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.urls import path
from token_auth_app.consumers import TokenAuthConsumer
from token_auth_app.middlewares import TokenAuthMiddleWare
  
application = ProtocolTypeRouter(
    {
        "websocket": TokenAuthMiddleWare(
            AllowedHostsOriginValidator(
                URLRouter(
                [path("", TokenAuthConsumer.as_asgi())]
                )
            )
        )
    }
)

Объяснение кода:

Использование различных модулей описано ниже:

  1. ProtocolTypeRouter : маршрутизирует сервер всего приложения в зависимости от типа протокола. Если используется протокол HTTP, он обслуживает приложение Django WSGI, а если WS — приложение ASGI.
  2. URLRouter : маршрутизирует маршруты подключения WS.
  3. AllowedHostsOriginValidator : этот стек безопасности используется для проверки соединения и принятия данных только с тех хостов, которые разрешены в файле settings.py приложения Django.
  4. path : Маршрутизация пути.
  5. TokenAuthConsumer : для обслуживания экземпляра потребителя для определенного соединения.
  6. TokenAuthMiddleware : для настройки экземпляра пользователя с использованием токена, переданного в соединение.

Затем установите стеки для обслуживания приложения ASGI в переменной приложения. Имейте в виду, что мы используем TokenAuthMiddleWare, и именно здесь мы можем установить пользователя с помощью токена. В приложении для аутентификации токенов middlewares.py мы недавно разработали этот класс.
Вы могли заметить, что мы не используем AuthMiddlewareStack. Это потому, что это полезно при обслуживании приложения ASGI из того же приложения или механизма шаблонов. Он хранит данные в сессиях.

Слои приложений и каналов ASGI в settings.py

Установите, что ваше приложение ASGI_APPLICATION будет обслуживать требуемых потребителей и маршрут. Принимая во внимание, что в CHANNEL_LAYERS мы используем InMemoryChannel, это для среды разработки.

Python3




WSGI_APPLICATION = "TokenAuth.wsgi.application"
ASGI_APPLICATION = "TokenAuth.routing.application"
  
  
CHANNEL_LAYERS = {
    "default": {
        "BACKEND":
      "channels.layers.InMemoryChannelLayer",
    }
}

token_auth_app/signals.py

Этот файл создает объект токена для каждого пользователя, который будет создан, и этот signal.py будет импортирован файлом apps.py в готовой функции.

Python3




from django.dispatch import receiver
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
  
  
@receiver(post_save, sender=User)
def create_token(sender, instance, created, **kwargs):
    if created:
        token = Token.objects.create(user=instance)
        token.save()

token_auth_app/apps.py

Теперь, после настройки всей серверной части приложения и базы данных, это добавит в работу модель токенов, сеансы и остальную структуру.

Python3




from django.apps import AppConfig
  
  
class TokenAuthAppConfig(AppConfig):
    default_auto_field = 
        "django.db.models.BigAutoField"
    name = "token_auth_app"
  
    def ready(self):
        import token_auth_app.signals

token_auth_app/views.py

Здесь мы создадим страницу входа для внешней части, и оттуда мы сделаем почтовый запрос с именем пользователя и паролем, затем бэкэнд-представление проверит, есть ли пользователь там или нет, если пользователь там, то токен будет отправлен из бэкэнд в ответе, и оттуда мы создадим сокетное соединение. Для этого нам понадобится файл views.py, который будет обрабатывать вход в систему и отправлять токен.

Использование различных модулей описано ниже:

  1. Ответ : это объект ответа, который отправляется из представления API.
  2. api_view : преобразует обычное представление Django в представление API.
  3. аутентификация : это вернет пользователя на основе имени пользователя и пароля.