Исключение внутри приложения: 'user' user=self.scope['user'] KeyError: 'user'

Я пытаюсь установить пользовательское промежуточное ПО для моих каналов django, поскольку AuthMiddleWare возвращает anonymousUser при попытке использовать self.scope['user] в consumers.py, и я использую аутентификацию на основе токенов, но после установки пользовательского промежуточного ПО django продолжает бросать это исключение:

Exception inside application: 'user'
Traceback (most recent call last):
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\staticfiles.py", line 44, in __call__
    return await self.application(scope, receive, send)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\routing.py", line 71, in __call__
    return await application(scope, receive, send)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\security\websocket.py", line 37, in __call__
    return await self.application(scope, receive, send)
  File "C:\Users\user\Easylance\chat\token_auth.py", line 29, in __call__
    return await super().__call__(scope, receive, send)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\middleware.py", line 26, in __call__
    return await self.inner(scope, receive, send)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\routing.py", line 150, in __call__
    return await application(
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 94, in app
    return await consumer(scope, receive, send)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 58, in __call__
    await await_many_dispatch(
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\utils.py", line 51, in await_many_dispatch
    await dispatch(result)
  File "C:\Users\user\Easylance\EasylanceEnv\lib\site-packages\channels\consumer.py", line 73, in dispatch
    await handler(message)
  File "C:\Users\user\Easylance\chat\consumers.py", line 13, in websocket_connect
    user=self.scope['user']
KeyError: 'user'

определенно, ошибка означает, что нет ключа для области dict с именем 'user', но в моем файле token_auth.py, где находится пользовательский промежуточный продукт, я действительно назначил ключ 'user', код пользовательского промежуточного продукта приведен ниже:




class TokenAuthMiddleware(BaseMiddleware):
    

    def __init__(self, inner):
        super().__init__(inner)

    async def __call__(self, scope,receive,send):
        headers = dict(scope['headers'])
        if b'authorization' in headers:
            try:
                token_name, token_key = headers[b'authorization'].decode().split()
                if token_name == 'Token':
                    token = Token.objects.get(key=token_key)
                    scope['user'] = token.user
                    
                    close_old_connections()
            except Token.DoesNotExist:
                scope['user'] = AnonymousUser()
                
        return await super().__call__(scope, receive, send)

Вы можете видеть в моем методе call я назначил scope['user'] как token.user, я не могу понять, что именно я сделал неправильно. Вот мой файл routing.py:

django_asgi_app = get_asgi_application()

application=ProtocolTypeRouter({
    
    "http": django_asgi_app,
    
    "websocket": AllowedHostsOriginValidator(
            TokenAuthMiddleware(
            URLRouter([
            url(r"^chat/(?P<username>\w+)/$",SendAndReceiveMessage.as_asgi()),
        ])
            )
            
        
        ),
    })

мой файл consumer.py:

class SendAndReceiveMessage(AsyncConsumer):
    async def websocket_connect(self, event):
        
        other_username=self.scope['url_route']['kwargs']['username']
        self.other_username=other_username
        user=self.scope['user']
        print(user,other_username)
        unique_obj=await self.get_unique_obj(user,other_username)
        self.unique_obj=unique_obj
        print(unique_obj.id)
        chat_id=f"unique_id_{unique_obj.id}"
        self.chat_id= f"unique_id_{unique_obj.id}"
        await self.channel_layer.group_add(
            chat_id,
            self.channel_name
            )
        
        await self.send({
            "type": "websocket.accept",
        })
        
    async def websocket_receive(self,event):
        print("receive",event)
        new_message=event.get('message',None)
        if new_message != None:
            new_message=json.loads(new_message)
            user=self.scope['user']
            if user.is_authenticated:
                username=new_message.get('username')
                response={
                        'username':new_message.get('username'),
                        'message':new_message.get('text')
                        
                        }
            
                await self.channel_layer.group_send(
                    self.chat_id,
                    {
                      "type" : "send_message",
                       "received": json.dumps(response)
                        }
                    )
                if user.username==username:
                    other_user=self.get_user_obj(self.other_username)
                    await self.add_message(self.unique_obj,user, other_user, new_message.get('text'))
                else:
                    other_user=self.get_user_obj(self.other_username)
                    await self.add_message(self.unique_obj,other_user,user,new_message.get('text'))
                    
    
    async def send_message(self,event):
        await self.send({
            "type":"websocket.send",
            "text":event['received']
            })
    
    @database_sync_to_async
    def get_unique_obj(self,user,other_username):
        return UniqueHolder.objects.get_or_new(user,other_username)
    @database_sync_to_async
    def add_message(self,unique_obj,user1,user2,msg):
        Message.objects.create(uniqueH=unique_obj,sender=user1,receiver=user2,msg=msg)
    
    @database_sync_to_async
    def get_user_obj(self,username):
        return User.objects.get(username=username)
        

Чтобы отправить токен в веб-сокетном соединении через Postman, вы можете включить его в качестве параметра запроса в URL-адрес веб-сокета.

Например, если URL вашего вебсокета имеет вид ws://localhost:8000/path,, вы можете добавить токен в качестве параметра запроса следующим образом:

ws://localhost:8000/path?token=401f7ac837da42b97f613d789819ff93537bee6a

В Postman вы можете задать этот URL в поле "Websocket URL" при выполнении запроса websocket.

Вы можете сгенерировать токен, указав url в корневом файле urls.py и сгенерировав токен вначале:

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomObtainAuthTokenView(ObtainAuthToken):
    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data["user"]
        token, created = Token.objects.get_or_create(user=user)
        return Response({"token": token.key, "username": user.username})

urls.py

from .views import CustomObtainAuthTokenView

urlpatterns = [
    ...
    path("auth/", CustomObtainAuthTokenView.as_view(), name="auth"),
    ...
]

Сделайте POST-запрос к пользовательскому представлению аутентификации, передав имя и пароль пользователя: через postman, или вы можете сделать это следующим образом:

curl -X POST -H "Content-Type: application/json" -d '{"username": "admin", "password": "password123"}' http://localhost:8000/auth/

Or using a REST client like Postman:

Set the request method to POST
Set the request header Content-Type to application/json
In the request body, provide a JSON object with the username and password of the user, e.g. {"username": "admin", "password": "password123"}
Send the request to the URL http://localhost:8000/auth/
The response should contain the token, which you can use to authenticate subsequent requests.

Note: Make sure you have the djangorestframework and djangorestframework-jwt packages installed, and have them added to your INSTALLED_APPS and REST_FRAMEWORK settings in Django.
Вернуться на верх