django3 You cannot call this from an async context - use a thread or sync_to_async

django3 中使用 websocket 使用JWT 认证

django1.11 django2 中的认证 

原来django1.11 django2 中使用的认证不能使用了 报错

 You cannot call this from an async context - use a thread or sync_to_async

首先我把jwt 写入了cookie 在登陆的时候 名称为token

class UserLoginViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet):
	"""create:2 用户登录"""
	queryset = Users.objects.filter(is_active=True)
	serializer_class = LoginSerializer

	def create(self, request, *args, **kwargs):
		serializer = self.get_serializer(data=request.data)
		serializer.is_valid(raise_exception=True)
		username = serializer.validated_data['username']
		password = serializer.validated_data['password']

		user = authenticate(username=username, password=password)
		headers = self.get_success_headers(serializer.data)
		return_dict = serializer.data
		if user and user.is_active:
			payload = jwt_payload_handler(user)
			jwt = jwt_encode_handler(payload)
			return_dict["token"] = jwt
			return_dict['id'] = user.id
			return_dict['name'] = user.name
			times = JWT_AUTH.get("JWT_EXPIRATION_DELTA")
			res = Response(return_dict, status=status.HTTP_201_CREATED, headers=headers)
			res.set_cookie("token", jwt, expires=times.seconds+times.days*86400)
			return res

		return_dict['msg'] = "用户名或者密码错误"
		return Response(return_dict, status=status.HTTP_401_UNAUTHORIZED, headers=headers)

修改认证类 使用async


from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser

from rest_framework.authtoken.models import Token
from rest_framework_jwt.authentication import jwt_decode_handler


def authenticate(jwt_value):
	try:
		payload = jwt_decode_handler(jwt_value)
		username = payload.get('username')
		if username:
			User = get_user_model()
			user = User.objects.get(username=username)
			return user
	except Exception:
		pass
	return AnonymousUser()


@database_sync_to_async
def get_user(headers):
	try:
		items = headers[b'cookie'].decode().split("; ")
		cookie_json = {}
		for i in items:
			splis = i.split("=")
			if len(splis) == 2:
				cookie_json[splis[0]] = splis[1]
		token = cookie_json.get("token")
		if token:
			return authenticate(token)

	except Exception:
		return AnonymousUser()


class JWTAuthMiddleware:

	def __init__(self, inner):
		self.inner = inner

	def __call__(self, scope):
		return JWTAuthMiddlewareInstance(scope, self)


class JWTAuthMiddlewareInstance:
	"""
	Yeah, this is black magic:
	https://github.com/django/channels/issues/1399
	"""

	def __init__(self, scope, middleware):
		self.middleware = middleware
		self.scope = dict(scope)
		self.inner = self.middleware.inner

	async def __call__(self, receive, send):
		headers = dict(self.scope['headers'])

		if b'cookie' in headers:
			self.scope['user'] = await get_user(headers)
		inner = self.inner(self.scope)
		return await inner(receive, send)


JWTAuthMiddlewareStack = lambda inner: JWTAuthMiddleware(AuthMiddlewareStack(inner))

使用认证

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter, ChannelNameRouter
from django.contrib.auth.models import AnonymousUser

import users.routing
from users import consumers
from utils.async_auth import JWTAuthMiddlewareStack
# from utils.ws_authentication import QueryAuthMiddleware

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': JWTAuthMiddlewareStack(
        URLRouter(
            users.routing.websocket_urlpatterns
        )
    ),
    "channel": ChannelNameRouter({
        "service-detection": consumers.ServiceConsumer,
    }),
})

上面的 JWTAuthMiddlewareStack 有一个问题,当token 不存在是  get_user(headers) 获取不到返回值为None,再去验证AuthMiddlewareStack 里面的SessionMiddleware 的时候会报错,在session登录的情况下不能验证此websocket已登录

修改如下

class JWTAuthMiddlewareInstance:
	"""
	Yeah, this is black magic:
	https://github.com/django/channels/issues/1399
	"""

	def __init__(self, scope, middleware):
		self.middleware = middleware
		self.scope = dict(scope)
		self.inner = self.middleware.inner

	async def __call__(self, receive, send):
		headers = dict(self.scope['headers'])

		if b'cookie' in headers:
			user = await get_user(headers)
			if user != None:
				self.scope['user'] = user

		inner = self.inner(self.scope)
		return await inner(receive, send)

猜你喜欢

转载自blog.csdn.net/weixin_37989267/article/details/105709153