如何在Django渠道中使用Django的会话身份验证?

时间:2019-11-10 10:43:42

标签: django reactjs authentication websocket django-channels

我正在使用Django,Django Channels和React开发国际象棋Web应用程序。我正在使用websocket在在线玩家之间进行游戏,并获取有关哪些玩家现在在线且可以玩的更新。但是,我停留在身份验证部分。我首先从令牌认证开始,但是我发现无法将自定义标头连同令牌一起发送到websocket请求中。然后,我回到默认的django.contrib.auth会话身份验证。不幸的是,当客户端登录并连接到Websocket时,我无法获得其用户信息,就好像用户正在使用与Websocket的其他会话一样。当我在websocket使用者中打印 self.scope [“ user”] 时,得到的值是 AnonymousUser 。请注意,我能够使用websocket交换消息,并且身份验证与普通的HTTP请求配合得很好,因为我可以防止未登录的用户访问视图。

我猜测问题与以下事实有关:客户端上的websocket请求不会像http请求那样访问或使用cookie进行身份验证。

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

这是我在响应中发送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
        }))

1 个答案:

答案 0 :(得分:0)

我认为您是对的,您可能没有用于来自客户端的请求的会话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);
        });
    });

我希望这会有所帮助。如果没有,让我知道。