【问题标题】:How to use Django's session authentication with Django Channels?如何在 Django Channels 中使用 Django 的会话身份验证?
【发布时间】:2020-03-06 08:13:27
【问题描述】:

我正在使用 Django、Django Channels 和 React 开发一个国际象棋网络应用程序。我正在使用 websockets 来进行在线玩家之间的游戏,以及更新哪些玩家现在在线并且可以玩。但是我被困在身份验证部分。我首先从令牌身份验证开始,但我发现无法将带有令牌的自定义标头作为 websocket 请求的一部分发送。然后我回到默认的 django.contrib.auth 会话身份验证。不幸的是,当客户端登录并连接到 websocket 时,我无法获取他们的用户信息,就好像用户正在使用与 websocket 的不同会话一样。当我在 websocket 消费者中打印 self.scope["user"] 时,我得到了值 AnonymousUser。请注意,我可以使用 websocket 交换消息,并且身份验证适用于正常的 http 请求,因为我可以阻止未登录的用户访问视图。

我猜这个问题与客户端的 websocket 请求没有像 http 请求一样访问或使用 cookie 进行身份验证有关。

有没有人遇到过类似的问题,他们是如何解决的?

这就是我在 react 中发送 websocket 消息的方式:

submitMessage = (evt) => {
    //console.log("token in Messenger=",this.props.token);
    evt.preventDefault();
    const message = { message: this.state.message_to_send}
    this.ws.send(JSON.stringify(message))
  }

这是处理 websocket 请求的后端代码:

from channels.generic.websocket import WebsocketConsumer
import json
from asgiref.sync import async_to_sync

class LoggedUsersConsumer(WebsocketConsumer):
    def connect(self):
        self.user = self.scope["user"]
        print(self.scope)
        print(self.user,"+++++++++++++")
        #Join group
        async_to_sync(self.channel_layer.group_add)(
            "logged_users",
            self.channel_name
        )
        self.accept()

    def disconnect(self, close_code):
        async_to_sync(self.channel_layer.group_discard)(
            "logged_users",
            self.channel_name
        )

    def receive(self, text_data):
        self.user = self.scope["user"]
        print(self.user,"+++++++++++++")

        text_data_json = json.loads(text_data)
        print(text_data_json)
        message = text_data_json['message']

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            "logged_users",
            {
                'type': 'logged_user_message',
                'message': message
            }
        )

    def logged_user_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))

【问题讨论】:

    标签: django reactjs authentication websocket django-channels


    【解决方案1】:

    我认为您是对的,您可能没有用于客户端请求的会话 cookie,这就是您获得 AnonymousUser 的原因。我认为这与您在 React 和 Django 中处理 websocket 请求的方式没有任何关系。

    请在您的 React 前端检查您浏览器的 cookie(通过 Chrome/Firefox 中的开发者工具)。您应该至少有 2 个 cookie,csrftoken 和 sessionid。如果缺少其中任何一个,以下内容可能会帮助您朝着正确的方向前进。在使用 Vue、Django Channels 和 Django Rest Framework 进行开发时,我也遇到了同样的情况。

    如果您通过浏览器访问 Django 后端,HTML 模板和浏览器会负责设置 cookie。当您从 React 或 Vue 执行此操作时,不会呈现 HTML。因此,您需要自己实现身份验证和 cookie 的设置。当然,您需要在稍后用于访问网络套接字的同一会话中从 React 进行身份验证。

    我使用以下 Django 视图从前端进行身份验证:

    @api_view()
    @permission_classes([AllowAny])
    @ensure_csrf_cookie
    @csrf_exempt
    def session_info(request):
        """
        View to retrieve user info for current user. (Can be adapted to your needs). If user is not logged in, view will
        still return CSRF cookie which in neccessary for authentication.
        """
        if not request.user.is_authenticated:
            return Response({"message": "Not authenticated.", "authenticated": False})
        return Response(
            {"message": "Authenticated.", "authenticated": True, "user": str(request.user)}
        )
    
    
    @api_view(['POST'])
    @permission_classes([AllowAny])
    def session_auth(request):
        """
        Login-view.
        """
        username = request.data['username']
        password = request.data['password']
        user = authenticate(request, username=username, password=password)
        if user is not None:
            if user.is_active:
                login(request, user)
                request.session['authenticated_user'] = user.username
                return Response(
                    {
                        "message": "Authenticated.",
                        "authenticated": True,
                        "name": user.name,
                    }
                )
        return Response({"message": "Not authenticated", "authenticated": False})
    
    
    

    在您的 urls.py 中,您需要添加如下内容:

    urlpatterns = [
        path(
            'session/',
            views.session_info,
            name='session',
        ),
        path(
            'sessionauth/',
            views.session_auth,
            name='sessionauth',
        ),
    ]
    

    现在你可以从你的前端做这样的事情(以下是我的 javascript/Vue 代码,稍微适应了这篇文章,但你可能可以用 React 做类似的事情):

    // Please note: this code can probably be improved a lot as my skills in javascript are not the best
    login: ({ username, password }) => {
        window.$cookies.set("username", username);
        const session_url = `${BACKEND_URL}/api/v1/session/`;
        const url = `${BACKEND_URL}/api/v1/sessionauth/`;
    
        axios.get(session_url).then(response => {      
          axios.defaults.headers.common["Authorization"] = "";
          // NOTE: the CSRF-cookie need to be included in subsequent POSTs:
          axios.defaults.headers.post["X-CSRF-Token"] = response.data._csrf;
          axios
            .post(url, { username: username, password: password })
            .then(response => {
              axios.get(session_url);
            })
            .catch(e => {
              console.log("ERROR: ", e);
              commit("SET_LOGIN_ERROR", e);
            });
        });
    
    

    我希望这会有所帮助。如果没有,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-12
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 2020-09-29
      • 1970-01-01
      • 2011-02-09
      • 2017-03-15
      相关资源
      最近更新 更多