You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django Channels 1.x如何对WebSocket请求进行用户认证?

嘿,针对你的问题,我来一步步帮你梳理清楚——因为你用的是Django 1.11.20 + Channels 1.1.8,这个版本的Channels和现在的2.x+差异挺大的,得针对性解决:

为什么channel_session_user_from_http不适合你的场景?

首先,这个装饰器的核心逻辑是从Django的Session机制里提取用户——它默认假设用户是通过Cookie-based的session完成认证的(比如普通的Django登录)。但你用的是JWT Token,属于无状态认证,不存在服务器端的Session记录,所以这个装饰器根本没法从JWT里拿到用户信息,完全不符合你的需求,得自定义认证逻辑。

客户端如何传递JWT Token?

React Native的原生WebSocket API有个限制:无法设置自定义请求头,所以你有两个靠谱的方案:

方案1:握手时通过URL Query参数传递(最简单)

在建立WebSocket连接时,把Token拼在URL后面:

// React Native客户端代码
const userToken = "你的JWT Token字符串";
const ws = new WebSocket(`ws://你的服务器地址/ws/chat/?token=${userToken}`);

// 后续的连接、消息处理逻辑
ws.onopen = () => console.log("连接成功");
ws.onmessage = (event) => console.log("收到消息:", event.data);

方案2:连接后发送第一条消息携带Token(更安全)

这种方式不会把Token暴露在URL里(避免被服务器日志记录),适合对安全性要求更高的场景:

// React Native客户端代码
const ws = new WebSocket("ws://你的服务器地址/ws/chat/");

ws.onopen = () => {
  // 连接成功后立即发送认证消息
  ws.send(JSON.stringify({
    type: "auth",
    token: "你的JWT Token字符串"
  }));
};

ws.onmessage = (event) => {
  const data = JSON.parse(event.data);
  if (data.type === "auth_success") {
    console.log("认证通过,可以开始聊天了");
  } else if (data.type === "chat_message") {
    console.log("收到聊天消息:", data.content);
  }
};
服务器端如何实现JWT认证?

假设你用的是djangorestframework-jwt来处理JWT(毕竟你HTTP请求用的是JWT),我们可以直接复用它的解码逻辑:

方案1:握手阶段认证(对应客户端方案1)

# consumers.py
from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
from rest_framework_jwt.settings import api_settings
import jwt
import json

# 复用DRF-JWT的解码函数
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        # 从URL Query参数里提取Token
        query_string = self.scope["query_string"].decode()
        token = None
        if "token=" in query_string:
            token = query_string.split("token=")[1]
        
        if token:
            try:
                # 解码JWT获取用户ID
                payload = jwt_decode_handler(token)
                user_id = payload.get("user_id")
                # 从数据库获取用户对象
                user = User.objects.get(id=user_id)
                # 将用户绑定到当前连接的scope中,后续可用
                self.scope["user"] = user
                # 认证通过,接受连接
                self.accept()
                return
            except (jwt.ExpiredSignature, jwt.DecodeError, User.DoesNotExist):
                # Token过期、无效或用户不存在,拒绝连接
                pass
        
        # 认证失败,关闭连接
        self.close()

    def receive(self, text_data):
        # 这里可以直接通过self.scope["user"]获取已认证的用户
        current_user = self.scope["user"]
        data = json.loads(text_data)
        # 处理聊天消息逻辑
        print(f"用户 {current_user.username} 发送了消息: {data['content']}")

方案2:第一条消息认证(对应客户端方案2)

# consumers.py
from channels.generic.websocket import WebsocketConsumer
from django.contrib.auth.models import User
from rest_framework_jwt.settings import api_settings
import jwt
import json

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        # 先接受连接,等待客户端发送认证消息
        self.accept()

    def receive(self, text_data):
        data = json.loads(text_data)
        
        # 处理认证请求
        if data.get("type") == "auth":
            token = data.get("token")
            try:
                payload = jwt_decode_handler(token)
                user_id = payload.get("user_id")
                user = User.objects.get(id=user_id)
                self.scope["user"] = user
                # 通知客户端认证成功
                self.send(text_data=json.dumps({
                    "type": "auth_success"
                }))
            except (jwt.ExpiredSignature, jwt.DecodeError, User.DoesNotExist):
                self.send(text_data=json.dumps({
                    "type": "auth_failed"
                }))
                self.close()
            return
        
        # 检查是否已完成认证
        if not hasattr(self.scope, "user") or self.scope["user"].is_anonymous:
            self.close()
            return
        
        # 处理正常聊天消息
        current_user = self.scope["user"]
        print(f"用户 {current_user.username} 发送了消息: {data['content']}")
额外注意事项
  1. 确保你的JWT Token包含user_id字段(DRF-JWT默认会包含),这样才能从payload里取出用户ID查询数据库。
  2. 认证失败时一定要关闭连接,避免未授权的客户端占用资源。
  3. 如果你的Token有过期时间,记得在客户端处理过期逻辑,重新获取Token后再建立连接。

内容的提问来源于stack exchange,提问作者Thanasis1101

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:14:21