django3 中使用 websocket 使用JWT 认证
原来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)