Essayer de faire en sorte que l'authentification fonctionne avec les canaux Django avec une application websockets très simple qui renvoie tout ce que l'utilisateur envoie avec un préfixe "You said: "
.
Mes processus:
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
J'exécute tous les processus lorsque je teste localement avec heroku local -e .env -p 8080
, mais vous pouvez également les exécuter tous séparément.
Remarque J'ai WSGI sur localhost:8080
et ASGI sur localhost:9090
.
Routage et consommateurs:
### routing.py ###
from . import consumers
channel_routing = {
'websocket.connect': consumers.ws_connect,
'websocket.receive': consumers.ws_receive,
'websocket.disconnect': consumers.ws_disconnect,
}
et
### 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 :(",
})
Et puis pour tester, je vais dans la console Javascript sur le domaine même que mon site HTTP, et tape:
> 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
Et le journal de mon serveur local indique:
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]
Ce qui, bien sûr, n'a aucun sens. Quelques questions:
message.user
comme une AnonymousUser
alors que l'ID utilisateur actuel _auth_user_id=1
(c'est mon ID utilisateur correct) dans la session?session_key=xxxx
dans ma connexion WebSocket - pourtant, Django a pu lire le cookie de mon navigateur pour le bon utilisateur, [email protected]
? Selon la documentation de Channels, cela ne devrait pas être possible .Note: Cette réponse est explicite pour channels 1.x
, channels 2.x
utilise un mécanisme d'authentification différent .
J'ai aussi eu du mal avec les canaux Django, j'ai dû creuser dans le code source pour mieux comprendre la documentation ...
Les docs mentionnent ce type de longue traînée de décorateurs qui s'appuient les uns sur les autres (http_session
, http_session_user
...) que vous pouvez utiliser pour envelopper vos consommateurs de messages. Au milieu de cette traînée, on peut lire ceci:
Une chose à noter est que vous obtenez uniquement les informations HTTP détaillées lors du message de connexion d'une connexion WebSocket (vous en saurez plus à ce sujet dans la spécification ASGI). Cela signifie que nous ne gaspillons pas la bande passante en envoyant les mêmes informations via la connexion. fil inutilement . Cela signifie également que nous devrons saisir l’utilisateur dans le gestionnaire de connexion, puis le stocker dans la session; ....
Il est facile de se perdre dans tout ça , du moins nous l'avons tous les deux ...
Vous devez simplement vous rappeler que cela se produit lorsque vous utilisez channel_session_user_from_http
:
http_session_user
http_session
qui analysera le message et nous donnera un attribut message.http_session
.message.user
basé sur les informations qu'il a reçues dans message.http_session
(cela vous piquera plus tard) channel_session
qui lancera une session fictive dans message.channel_session
et le liera au canal de réponse au message. transfer_user
qui déplacera le http_session
dans le channel_session
Cela se produit lors de la gestion de la connexion d'un websocket. Ainsi, lors des messages suivants, vous n'aurez pas accès à des informations HTTP détaillées. Par conséquent, après la connexion, vous appelez à nouveau channel_session_user_from_http
, ce qui dans cette situation (messages post-connexion). appelle http_session_user
qui essaiera de lire les informations Http mais échouera, entraînant définissant message.http_session
sur None
et remplaçant message.user
par AnonymousUser
.
C'est pourquoi vous devez utiliser channel_session_user
dans ce cas.
Les chaînes peuvent utiliser les sessions Django à partir de cookies (si vous utilisez votre serveur Websocket sur le même port que votre site principal, en utilisant quelque chose comme Daphne) ou d'un paramètre GET session_key, qui fonctionne si vous souhaitez continuer à exécuter vos demandes HTTP. via un serveur WSGI et de décharger WebSockets vers un second processus serveur sur un autre port.
Rappelez-vous http_session
, ce décorateur qui nous obtient les données message.http_session
? il semble que s'il ne trouve pas le paramètre session_key
GET, il ne réussit pas à settings.SESSION_COOKIE_NAME
, qui est le cookie sessionid
régulier. Ainsi, que vous fournissiez session_key
ou non, vous serez toujours connecté si vous êtes connecté. Bien sûr, cela ne se produit que lorsque vos serveurs ASGI et WSGI se trouvent sur le même domaine (127.0.0.1 dans ce cas), la différence de ports n’importe pas .
Je pense que la différence que les docs essaient de communiquer mais ne s'est pas développée, c'est que vous devez configurer le paramètre session_key
GET lorsque vous avez vos serveurs ASGI
et WSGI
sur différents domaines, car les cookies sont limités par le domaine et non par le port.
En raison de ce manque d’explication, j’ai dû tester ASGI et WSGI sur le même port et sur un port différent et le résultat était identique, j’étais toujours authentifié, je changeais le domaine du serveur en 127.0.0.2
au lieu de 127.0.0.1
et l’authentification avait disparu. session_key
get paramètre et l'authentification était de retour.
Mise à jour: une rectification du paragraphe docs était juste poussée dans le dépôt des canaux, elle était censée mentionner le domaine au lieu du port comme je l’ai mentionné.
ma réponse est la même que celle de turbotux mais plus longue, vous devriez utiliser @channel_session_user_from_http
sur ws_connect et @channel_session_user
sur ws_receive et ws_disconnect, rien de ce que vous avez montré ne dit que cela ne fonctionnera pas si vous faites cette modification, essayez peut-être de supprimer http_user=True
de votre consommateur de réception? même si je soupçonne qu’il n’a aucun effet depuis son contenu non documenté et destiné uniquement à être utilisé par les consommateurs génériques ...
J'espère que cela t'aides!
Pour répondre à votre première question, vous devez utiliser:
channel_session_user
décorateur dans les appels de réception et de déconnexion.
channel_session_user_from_http
appelle la session transfer_user pendant la méthode de connexion pour transférer la session http à la session de canal. De cette manière, tous les futurs appels pourront accéder à la session de canal pour récupérer les informations de l'utilisateur.
En ce qui concerne votre deuxième question, je pense que ce que vous voyez est que la bibliothèque de socket Web par défaut transmet les cookies du navigateur via la connexion.
Troisièmement, je pense que votre installation fonctionnera assez bien une fois que les décorateurs auront changé.