2017-02-04 5 views
12

Попытка получить аутентификацию, работающую с каналами Django, с помощью очень простого приложения для веб-сайтов, которое отсылает все, что пользователь отправляет с префиксом "You said: ".Аутентификация сеанса с каналами Django

Мои процессы:

web: gunicorn myproject.wsgi --log-file=- --pythonpath ./myproject 
realtime: daphne myproject.asgi:channel_layer --port 9090 --bind 0.0.0.0 -v 2 
reatime_worker: python manage.py runworker -v 2 

Я бегу все процессы при тестировании на местном уровне с heroku local -e .env -p 8080, но вы также можете запустить их все по отдельности.

Примечание. У меня есть WSGI на localhost:8080 и ASGI на localhost:9090.

маршрутизации и потребители:

### routing.py ### 

from . import consumers 

channel_routing = { 
    'websocket.connect': consumers.ws_connect, 
    'websocket.receive': consumers.ws_receive, 
    'websocket.disconnect': consumers.ws_disconnect, 
} 

и

### consumers.py ### 

import traceback 

from django.http import HttpResponse 
from channels.handler import AsgiHandler 

from channels import Group 
from channels.sessions import channel_session 
from channels.auth import channel_session_user, channel_session_user_from_http 

from myproject import CustomLogger 
logger = CustomLogger(__name__) 

@channel_session_user_from_http 
def ws_connect(message): 
    logger.info("ws_connect: %s" % message.user.email) 
    message.reply_channel.send({"accept": True}) 
    message.channel_session['prefix'] = "You said" 
    # message.channel_session['django_user'] = message.user # tried doing this but it doesn't work... 

@channel_session_user_from_http 
def ws_receive(message, http_user=True): 
    try: 
     logger.info("1) User: %s" % message.user) 
     logger.info("2) Channel session fields: %s" % message.channel_session.__dict__) 
     logger.info("3) Anything at 'django_user' key? => %s" % (
      'django_user' in message.channel_session,)) 

     user = User.objects.get(pk=message.channel_session['_auth_user_id']) 
     logger.info(None, "4) ws_receive: %s" % user.email) 

     prefix = message.channel_session['prefix'] 

     message.reply_channel.send({ 
      'text' : "%s: %s" % (prefix, message['text']), 
     }) 
    except Exception: 
     logger.info("ERROR: %s" % traceback.format_exc()) 

@channel_session_user_from_http 
def ws_disconnect(message): 
    logger.info("ws_disconnect: %s" % message.__dict__) 
    message.reply_channel.send({ 
     'text' : "%s" % "Sad to see you go :(", 
    }) 

А затем, чтобы проверить, я иду в Javascript консоли на одном домене как мой HTTP сайт, и введите:

> var socket = new WebSocket('ws://localhost:9090/') 
> socket.onmessage = function(e) {console.log(e.data);} 
> socket.send("Testing testing 123") 
VM481:2 You said: Testing testing 123 

И показывает мой локальный журнал сервера:

ws_connect: [email protected] 

1) User: AnonymousUser 
2) Channel session fields: {'_SessionBase__session_key': 'chnb79d91b43c6c9e1ca9a29856e00ab', 'modified': False, '_session_cache': {u'prefix': u'You said', u'_auth_user_hash': u'ca4cf77d8158689b2b6febf569244198b70d5531', u'_auth_user_backend': u'django.contrib.auth.backends.ModelBackend', u'_auth_user_id': u'1'}, 'accessed': True, 'model': <class 'django.contrib.sessions.models.Session'>, 'serializer': <class 'django.core.signing.JSONSerializer'>} 
3) Anything at 'django_user' key? => False 
4) ws_receive: [email protected] 

Что, конечно, не имеет смысла. Несколько вопросов:

  1. Почему бы Джанго увидеть message.user как AnonymousUser, но имеют фактический идентификатор пользователя _auth_user_id=1 (это мой правильный идентификатор пользователя) в сессии?
  2. Я запускаю свой локальный сервер (WSGI) на 8080 и дафни (ASGI) на 9090 (разные порты). И я не включил session_key=xxxx в мое соединение с WebSocket - но Django смог прочитать cookie моего браузера для правильного пользователя, [email protected]? According to Channels docs, this shouldn't be possible.
  3. Под моей настройкой, какой лучший/самый простой способ выполнить аутентификацию с каналами Django?

ответ

0

Чтобы ответить на ваш первый вопрос, который вы должны использовать:

channel_session_user 

декоратора в приемных и отключают вызовов.

channel_session_user_from_http 

вызывает сеанс transfer_user во время метода подключения для передачи сеанса http в сеанс канала. Таким образом, все будущие вызовы могут получить доступ к сеансу канала для извлечения информации о пользователе.

К вашему второму вопросу я верю, что то, что вы видите, это то, что библиотека веб-сокетов по умолчанию передает куки-файлы браузера через соединение.

В-третьих, я думаю, что ваша установка будет работать достаточно хорошо, как только вы измените декораторы.

+1

Пробовал это - ничего не меняет. Я все равно получаю тот же самый объект 'AnonymousUser'. – lollercoaster

+0

Использование '@ http_session_user' Я могу получить надлежащий объект' message.user', но тогда атрибут 'message.channel_session' не существует. – lollercoaster

14

Примечание: Этот ответ явно на channels 1.x, channels 2.x использует different auth mechanism.


У меня было трудное время с Джанго каналов тоже, я должен был вырыть в исходный код, чтобы лучше понять документы ...

Вопрос 1:

Документах упоминает такого рода длинный след декораторов, опирающихся друг на друга (http_session, http_session_user ...), которые вы можете использовать, чтобы обернуть потребителей сообщение, в середине этого следа в нем говорится следующее:

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

Его легко заблудиться в all that, по крайней мере, мы оба сделали ...

вы просто должны помнить, что это происходит, когда вы используете channel_session_user_from_http:

  1. Он называет http_session_user
    а. звонит http_session, который проанализирует сообщение и даст нам атрибут message.http_session.
    b. По возвращению из вызова, он инициирует message.user на основе информации, которую он получил в message.http_session (это будет укусить вас позже)
  2. Он называет channel_session, который будет инициировать фиктивную сессию в message.channel_session и привязывает его к каналу ответного сообщения.
  3. Теперь она называет transfer_user, который будет двигаться http_session в channel_session

Это происходит во время обработки подключения WebSocket, так и на последующих сообщений, которые вы не будете иметь доступ до подробной информации HTTP, так что происходит после того, как соединение заключается в том, что вы снова вызываете channel_session_user_from_http, который в этой ситуации (сообщения после подключения) вызывает http_session_user, который попытается прочитать информацию Http, но не даст результата setting message.http_session to None and overriding message.user to AnonymousUser.
Поэтому в этом случае вам необходимо использовать channel_session_user.

Вопрос 2:

каналов можно использовать Django сессии либо из печенья (если вы используете сервер WebSocket на тот же порт, как ваш основной сайт, используя что-то вроде Дафна), или из session_key GET параметр, который работает, если вы хотите продолжать выполнять свои HTTP-запросы через сервер WSGI и разгружать WebSockets на второй серверный процесс на другом порту.

Запомните http_session, этот декоратор, который доставит нам данные message.http_session?кажется, что если он не находит параметр session_key GET, это fails to settings.SESSION_COOKIE_NAME, который является обычным файлом sessionid, так что независимо от того, предоставляете ли вы session_key или нет, вы все равно подключитесь, если вы вошли в систему, конечно, это произойдет только тогда, когда ваши серверы ASGI и WSGI находятся в одном домене (127.0.0.1 в этом случае), the port difference doesn't matter.

Я думаю, что разница, что документы пытаются общаться, но не расширить, что вам нужно настроить session_key параметр GET, имея ваши ASGI и WSGI серверов в разных доменах, так как куки ограничены домен не порт.

В связи с отсутствием объяснений мне пришлось протестировать ASGI и WSGI на одном и том же порту и другом порту, и результат был таким же, я все еще получал аутентификацию, менял один домен сервера на 127.0.0.2 вместо 127.0.0.1, и аутентификация была ушел, установите параметр session_key и аутентификация снова вернулась.

Обновление: Исправление абзаца документов было just pushed каналам репо, оно предназначалось для обозначения домена вместо порта, как я уже упоминал.

Вопрос 3:

мой ответ такой же, как turbotux, но дольше, вы должны использовать @channel_session_user_from_http на ws_connect и @channel_session_user на ws_receive и ws_disconnect, ничего из того, что вы показали не говорит, что это не будет работать, если вы сделаете это измените, возможно, попробуйте удалить http_user=True от вашего получателя? даже если вы подозреваете, что он не имеет никакого эффекта, поскольку он не документирован и предназначен только для использования универсальными потребителями ...

Надеюсь, это поможет!

+0

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

+1

Чувствует себя здоровым, чтобы помочь! – HassenPy