尝试使用带有django频道的websockets创建一些应用。 我有2个websocket客户端 - 一个是web界面/ js app,另一个是python应用程序。我想为他们(他们的消费者)提供不同的授权要求(最好是使用AuthMiddlewareStacks的方式)
我该如何实现?无法在文档https://channels.readthedocs.io/en/latest/topics/routing.html
中找到答案这是一个' sketch'。 (routing.py
不会以这种方式工作)。
我使用DRF,DRF-JWT,django频道2,如果它是相关的。
appmain.routing.py
application = ProtocolTypeRouter({
# (http->django views is added by default)
'websocket': AuthMiddlewareStack(
URLRouter(
app.routing.websocket_cli_urlpatterns
)
),
"websocket_0": TokenAuthMiddlewareStack(
URLRouter([
app.routing.websocket_web_urlpatterns
]),
)
})
app.routing.py
websocket_cli_urlpatterns = [
path('ws/app/<str:var1>/<str:var2>/', consumers.CliConsumer),
]
websocket_web_urlpatterns = [
path('ws/app/<str:var1>/', consumers.WebConsumer),
]
谢谢!
答案 0 :(得分:0)
编写您自己的身份验证处理程序,该处理程序结合了要使用的身份验证处理程序中的逻辑。 (您可以只调用它们的 call 方法,并且只有在两个都失败时才失败。)从GET参数或http标头中处理令牌,以便URL相同。
答案 1 :(得分:0)
# middleware.py
from urllib.parse import parse_qs
import jwt
from channels.auth import AuthMiddlewareStack
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from django.utils.translation import ugettext as _
from rest_framework import exceptions
from rest_framework_jwt.authentication import jwt_decode_handler, jwt_get_username_from_payload
class JWTAuthMiddleware():
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
query_dict = {k: v[0] for k, v in parse_qs(scope["query_string"].decode()).items()}
jwt_value = query_dict.get('token', None)
if jwt_value:
try:
try:
payload = jwt_decode_handler(jwt_value)
except jwt.ExpiredSignature:
msg = _('Signature has expired.')
raise exceptions.AuthenticationFailed(msg)
except jwt.DecodeError:
msg = _('Error decoding signature.')
raise exceptions.AuthenticationFailed(msg)
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed()
scope['user'] = self.authenticate_credentials(payload)
except exceptions.AuthenticationFailed:
scope['user'] = AnonymousUser()
return self.inner(scope)
def authenticate_credentials(self, payload):
"""
Returns an active user that matches the payload's user id and email.
"""
User = get_user_model()
username = jwt_get_username_from_payload(payload)
if not username:
msg = _('Invalid payload.')
raise exceptions.AuthenticationFailed(msg)
try:
user = User.objects.get_by_natural_key(username)
except User.DoesNotExist:
msg = _('Invalid signature.')
raise exceptions.AuthenticationFailed(msg)
if not user.is_active:
msg = _('User account is disabled.')
raise exceptions.AuthenticationFailed(msg)
close_old_connections()
return user
JWTAuthMiddlewareStack = lambda inner: JWTAuthMiddleware(AuthMiddlewareStack(inner))
#routing.py
from channels.routing import ProtocolTypeRouter, URLRouter
from core.middleware import JWTAuthMiddlewareStack
application = ProtocolTypeRouter({
'websocket':
JWTAuthMiddlewareStack(
URLRouter(apps.myapp.routing.websocket_urlpatterns)
)
})
答案 2 :(得分:0)
我做到了,而且奏效了:
# consumers.py
from channels.auth import AuthMiddlewareStack
class PrinterWSAuthMiddleWare:
"""
Token authorization middleware for Django Channels 2
"""
def __init__(self, inner):
self.inner = inner
def __call__(self, scope):
close_old_connections()
headers = dict(scope['headers'])
if b'authorization' in headers:
try:
token_name, token_key = headers[b'authorization'].decode().split()
if token_name == 'bearer':
printer = Printer.objects.select_related('user').get(auth_token=token_key)
scope['user'] = printer
else:
scope['user'] = None
except ObjectDoesNotExist:
scope['user'] = None
return self.inner(scope)
TokenAuthMiddlewareStack = lambda inner: PrinterWSAuthMiddleWare(AuthMiddlewareStack(inner))
# routing.py
...
'websocket': TokenAuthMiddlewareStack(
URLRouter(
api.ws_routing.websocket_urlpatterns
)
),
...
它没有记录,但是AuthMiddlewareStack
在后台检查Cookie和会话并设置范围(如果其中任何一个有效)。