如何在Django Channels中使用Knox令牌认证WebSocket连接?
在Django Channels中使用django-rest-knox进行WebSocket令牌认证
我明白你的困扰——Knox的令牌是加密存储的,没法直接用前端传的令牌字符串去数据库里查用户,必须借助Knox自身的验证机制来处理。下面我给你一步步拆解解决方案:
1. 编写自定义认证中间件
Channels的WebSocket连接是异步的,所以我们需要写一个异步的中间件,负责解析令牌、验证并把用户绑定到连接的scope中。
首先创建一个中间件文件(比如middleware.py),代码如下:
from channels.middleware import BaseMiddleware from channels.db import database_sync_to_async from urllib.parse import parse_qsl from django.contrib.auth.models import AnonymousUser from knox.models import AuthToken # 异步函数:通过Knox令牌获取用户 @database_sync_to_async def get_authenticated_user(token): try: # Knox令牌的前8位是存在数据库中的token_key,先通过它找到令牌对象 token_obj = AuthToken.objects.get(token_key=token[:8]) # 先检查令牌是否过期 if token_obj.expired(): return AnonymousUser() # 用Knox自带的token_compare方法验证完整令牌(因为数据库存的是哈希值,不能直接比对字符串) if token_obj.token_compare(token): return token_obj.user else: return AnonymousUser() except AuthToken.DoesNotExist: # 令牌不存在或无效 return AnonymousUser() # Channels认证中间件 class KnoxTokenAuthMiddleware(BaseMiddleware): async def __call__(self, scope, receive, send): # 从WebSocket连接的查询参数中提取令牌 query_params = dict(parse_qsl(scope["query_string"].decode())) raw_token = query_params.get("token", "") # 验证令牌并获取用户 scope["user"] = await get_authenticated_user(raw_token) # 继续处理连接 return await super().__call__(scope, receive, send)
代码解释:
database_sync_to_async:把同步的数据库操作包装成异步,适配Channels的异步环境。token[:8]:Knox生成的令牌前8位是唯一标识(存在数据库的token_key字段),后面的部分是哈希后的内容,所以先通过前8位找到对应的令牌对象。token_compare():Knox提供的方法,会把前端传的原始令牌哈希后和数据库中存储的哈希值比对,确保令牌有效。expired():检查令牌是否过期,避免使用已失效的令牌。
2. 配置Channels路由
在你的Channels路由文件(比如routing.py)中,用刚才写的中间件包裹WebSocket路由:
from channels.routing import ProtocolTypeRouter, URLRouter from django.urls import path from . import consumers from .middleware import KnoxTokenAuthMiddleware application = ProtocolTypeRouter({ # WebSocket路由配置 "websocket": KnoxTokenAuthMiddleware( URLRouter([ path("ws/your-path/", consumers.YourConsumer.as_asgi()), # 其他WebSocket路由... ]) ), })
这样所有WebSocket连接都会先经过Knox令牌认证,认证通过后,你就可以在Consumer中通过self.scope["user"]获取当前用户了。
3. 前端传递令牌
前端在建立WebSocket连接时,需要把Knox令牌作为查询参数传递,比如:
// 假设你已经从后端拿到了Knox令牌,存在knoxToken变量中 const socket = new WebSocket(`ws://your-domain.com/ws/your-path/?token=${knoxToken}`);
注意事项
- 安全问题:生产环境一定要用
wss(WebSocket Secure)协议,避免令牌明文传输。 - 令牌过期处理:如果令牌过期,中间件会返回
AnonymousUser,你可以在Consumer中判断用户是否匿名,主动关闭连接或返回错误信息。 - 令牌刷新:如果你的应用使用Knox的令牌刷新机制,前端需要在令牌过期前刷新,并重新建立WebSocket连接。
内容的提问来源于stack exchange,提问作者Brian Min
相关产品推荐
相关产品推荐

