2017-06-07 4 views
0

ich Benutzer mit diesem Mixin bin Authentifizierung auf verbinden:Benutzer django Kanäle OAuth2 Halten authentifizie

import functools 

from channels.handler import AsgiRequest 
from rest_framework.exceptions import AuthenticationFailed 
from rest_framework.settings import api_settings 

authenticators = [auth() for auth in api_settings.DEFAULT_AUTHENTICATION_CLASSES] 


def rest_auth(func): 
    """ 
    Wraps a HTTP or WebSocket connect consumer (or any consumer of messages 
    that provides a "cookies" or "get" attribute) to provide a "http_session" 
    attribute that behaves like request.session; that is, it's hung off of 
    a per-user session key that is saved in a cookie or passed as the 
    "session_key" GET parameter. 
    It won't automatically create and set a session cookie for users who 
    don't have one - that's what SessionMiddleware is for, this is a simpler 
    read-only version for more low-level code. 
    If a message does not have a session we can inflate, the "session" attribute 
    will be None, rather than an empty session you can write to. 
    Does not allow a new session to be set; that must be done via a view. This 
    is only an accessor for any existing session. 
    """ 

    @functools.wraps(func) 
    def inner(message, *args, **kwargs): 
     # Make sure there's NOT a http_session already 
     try: 
      # We want to parse the WebSocket (or similar HTTP-lite) message 
      # to get cookies and GET, but we need to add in a few things that 
      # might not have been there. 
      if "method" not in message.content: 
       message.content['method'] = "FAKE" 
      request = AsgiRequest(message) 

     except Exception as e: 
      raise ValueError(
       f'Cannot parse HTTP message - are you sure this is a HTTP consumer? {e}') 
     # Make sure there's a session key 
     user = None 
     auth = None 
     auth_token = request.GET.get("token", None) 

     if auth_token: 
      # comptatibility with rest framework 
      request._request = {} 
      request.META["Authorization"] = "Bearer {}".format(auth_token) 
      for authenticator in authenticators: 
       try: 
        user_auth_tuple = authenticator.authenticate(request) 
       except AuthenticationFailed: 
        pass 

       if user_auth_tuple is not None: 
        message._authenticator = authenticator 
        user, auth = user_auth_tuple 
        break 
     message.user, message.auth = user, auth 
     result = func(message, *args, **kwargs) 
     return result 
    return inner 


def rest_token_user(func): 
    """ 
    Wraps a HTTP or WebSocket consumer (or any consumer of messages 
    that provides a "COOKIES" attribute) to provide both a "session" 
    attribute and a "user" attibute, like AuthMiddleware does. 
    This runs http_session() to get a session to hook auth off of. 
    If the user does not have a session cookie set, both "session" 
    and "user" will be None. 
    """ 

    @rest_auth 
    @functools.wraps(func) 
    def inner(message, *args, **kwargs): 
     # If we didn't get a session, then we don't get a user 
     if not hasattr(message, "auth"): 
      raise ValueError("Did not see a http session to get auth from") 
     return func(message, *args, **kwargs) 

    return inner 


class RestTokenConsumerMixin: 
    """ 
    Authenticate user with token. 
    This mixin has to be passed as the first parent in class definition. 
    Pass the token as query parameter eg. 
    ws://127.0.0.1:8000/chat/?token=<your_token> 
    """ 
    rest_user = True 
    # This has to be True in order to keep information about the user 

    def get_handler(self, message, **kwargs): 
     handler = super(RestTokenConsumerMixin, self).get_handler(message, **kwargs) 
     if self.rest_user: 
      handler = rest_token_user(handler) 
     return handler 

src: https://gist.github.com/leonardoo/9574251b3c7eefccd84fc38905110ce4

und ich kann es in meiner Klasse basiert Verbraucher in connect-Methode zuzugreifen:

from channels.generic.websockets import JsonWebsocketConsumer 
from common.authentication import RestTokenConsumerMixin 
from accounts.serializers import UserSerializer 


class ChatConsumer(RestTokenConsumerMixin, JsonWebsocketConsumer): 

    # Set to True if you want it, else leave it out 
    strict_ordering = False 
    channel_session = True 

    def connection_groups(self, **kwargs): 
     """ 
     Called to return the list of groups to automatically add/remove 
     this connection to/from. 
     """ 
     return ["test"] 

    def connect(self, message, **kwargs): 
     """ 
     Perform things on connection start 
     """ 
     message.channel_session['user'] = UserSerializer(instance=message.user).data 
     if message.user.is_authenticated() and not message.user.is_anonymous: 
      message.reply_channel.send({"accept": True}) 
     else: 
      self.disconnect({'Error': 'Not authenticated user'}) 

    def receive(self, content, **kwargs): 
     """ 
     Called when a message is received with decoded JSON content 
     """ 
     # Simple echo 
     self.send(self.message.user.username) 

    def disconnect(self, message, **kwargs): 
     """ 
     Perform things on connection close 
     """ 
     pass 

Wie Sie jetzt sehen können, um Informationen über den Benutzer zu behalten, verwende ich Serializer und channel_session. Gibt es eine Möglichkeit, um den Benutzer in der Lage zu erhalten und zu trennen, wie von http_user?

Und wird es effizienter als etwas wie: user = User.objects.get(id=self.message.channel_session['user']['id'])?

Antwort

0

ich es gelöst, indem eine einfache mixin erstellen:

from django.contrib.auth import get_user_model 


class KeepUserConsumerMixin: 
    channel_session = True 

    @staticmethod 
    def _save_user(func): 
     def wrapper(message, **kwargs): 
      if message.user is not None and message.user.is_authenticated(): 
       message.channel_session['user_id'] = message.user.id 
      return func(message, **kwargs) 
     return wrapper 

    def __getattribute__(self, name): 
     method = super().__getattribute__(name) 
     if name == 'connect': 
      return self._save_user(method) 
     return method 

    @property 
    def user(self): 
     if not hasattr(self, '_user'): 
      user_id = self.message.channel_session['user_id'] 
      self._user = get_user_model().objects.get(id=user_id) 
     return self._user 
+0

tun Sie django, Kanäle verwenden, django alle zusammen Rest Rahmen? funktioniert es gut? Ich möchte auch Benutzer mit Django oauth2 authentifizieren authentifizieren und nur wundern, wird es gut funktionieren. – nextdoordoc

+0

Ja. Ich benutze Django, Kanäle, Django Rest Framework alle zusammen und Django-Rest-Framework-Social-Oauth2 für die Authentifizierung. Diese Lösung funktioniert gut für mich – Quba

Verwandte Themen