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']}")
额外注意事项
- 确保你的JWT Token包含
user_id字段(DRF-JWT默认会包含),这样才能从payload里取出用户ID查询数据库。 - 认证失败时一定要关闭连接,避免未授权的客户端占用资源。
- 如果你的Token有过期时间,记得在客户端处理过期逻辑,重新获取Token后再建立连接。
内容的提问来源于stack exchange,提问作者Thanasis1101
相关产品推荐
相关产品推荐

