reactjs 如何在Django通道中使用Django的会话认证?

i2byvkas  于 2022-12-22  发布在  React
关注(0)|答案(2)|浏览(158)

我正在使用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
        }))
r8uurelv

r8uurelv1#

我认为你是对的,你可能没有一个会话cookie来处理来自客户端的请求,这就是为什么你得到了AnonymousUser。我不认为这与你在React和Django中处理WebSocket请求的方式有任何关系。
请检查你的React前端浏览器的cookies(通过Chrome/Firefox的开发者工具)。你应该至少有两个cookies,csrftoken和sessionid。如果缺少其中任何一个,下面的方法可能会帮助你找到正确的方向。我在使用Vue,Django Channels和Django Rest Framework开发时也有过同样的经历。
如果您通过浏览器访问Django后端,HTML模板和浏览器会负责设置cookie。当您从React或Vue进行此操作时,HTML不会呈现。因此,您需要自己实现身份验证和cookie设置。当然,您需要在稍后用于访问Web套接字的同一会话中从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})

在您的www.example.com中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);
        });
    });

我希望这对你有帮助。如果没有,请告诉我。

41zrol4v

41zrol4v2#

我再补充一句也许能帮上忙,
您总是得到AnonymousUser,因为您可能没有正确传递cookie标头。
使用会话身份验证,像这样传递会话头。

Cookie: csrftoken=wBD7Qri09dyAR8oMY8fuL1nqCOvGGmaO; sessionid=hpb8xx873holf3zl01wfe6f7511bhxqi

你可以试着

Set-Cookie: sessionid=hpb8xx873holf3zl01wfe6f7511bhxqi;
Set-Cookie: csrftoken=wBD7Qri09dyAR8oMY8fuL1nqCOvGGmaO;

那没用的

相关问题