问题是浏览器不支持在 websocket 升级时传递 jwt auth 标头,所以基本上就是这样。我前段时间遇到了这个问题,并提出了通过查询参数传递令牌的解决方案 - 请注意如果没有 TLS,这是完全不安全的当您在 URI 中公开身份验证时。我无法再访问确切的代码,但想法如下:
from channels.generic.websockets import JsonWebsocketConsumer
from channels.handler import AsgiRequest
from rest_framework_jwt.serializers import VerifyJSONWebTokenSerializer
from jwt.exceptions import InvalidTokenError
from rest_framework.exceptions import ValidationError
class Consumer(JsonWebsocketConsumer):
def connect(self, message, **kwargs):
# construct a fake http-like request object from the message
message.content.setdefault('method', 'FAKE')
request = AsgiRequest(message)
# validate the token
try:
VerifyJSONWebTokenSerializer().validate(request.GET)
super().connect(message, **kwargs)
except (KeyError, InvalidTokenError, ValidationError,):
# token is either not available or invalid
# so we disconnect the user
message.reply_channel.send({'close': True})
向消费者注册
channel_routing = [
...
route_class(Consumer, path=r'^my-ws-endpoint$'),
]
在浏览器端,您可以通过将令牌作为 websocket URI 中的查询参数传递来建立 websocket 连接:
let token: string = 'my-token'; // get the token
let wsHandler: $WebSocket = new $WebSocket('wss://example.com/my-ws-endpoint/?token=' + token, ...);
然后,您可以在类似于以下的装饰器中提取身份验证检查代码@channel_session_user_from_http
只需装饰您的连接例程,或者如果您使用基于类的路由,则将代码提取到 mixin 中。
我想重申一下,如果不使用加密,这种方法是完全不安全的,因此在生产中,您的 URI 应该以https/wss
.
Edit: 这里有一个相当不错的解决方案用于 DRF 令牌身份验证,适用于基于函数和基于类的路由。它的方法与我的方法几乎相同,构造一个请求对象并将其传递给身份验证器。