通过drf和jwt实现多方式登录
需求如下:
用户输入:用户名或手机号或邮箱 +密码都能登录,并签发token
代码书写:
model层配置(需要扩写auth_user表):
from django.contrib.auth.models import AbstractUser class User(AbstractUser): mobile = models.CharField(max_length=24) icon = models.ImageField(upload_to='icon', default='icon/default.png')
路由配置:
urlpatterns = [ path('login/', views.UserView.as_view()), ]
视图层有三种写法
基础版:
视图层配置:
class UserView(APIView): # 局部禁用 认证类和权限类 authentication_classes = () permission_classes = () def post(self, request): # 1 request取出用户名和密码 username = request.data.get('username') password = request.data.get('password') # 2 使用正则判断用用户名是邮箱,手机号还是用户名,分别去查询当前用户 if re.match(r'^1[3-9][0-9]{9}$', username): user = User.objects.filter(mobile=username).first() elif re.match(r'^.+@.+$', username): user = User.objects.filter(email=username).first() else: user = User.objects.filter(username=username).first() if user and user.check_password(password): # 3 校验密码 # 4 签发token refresh = TokenObtainPairSerializer.get_token(user) # 5 返回给前端 return Response({'code': 100, 'msg': '成功', 'access': str(refresh.access_token), 'refresh': str(refresh)}) else: return Response({'code': 101, 'msg': '用户名或密码错误'})
进阶版:
视图层配置:
from rest_framework.generics import GenericAPIView from .serializer import LoginSerializer class UserView(GenericAPIView): authentication_classes = () permission_classes = () serializer_class = LoginSerializer def post(self, request): ser = self.get_serializer(data=request.data) if ser.is_valid(): # 会执行字段自己的校验(没有),执行局部钩子(没有),执行全局钩子(写了:校验用户,签发token) # context 是视图类和序列化列之间沟通的桥梁 access = ser.context.get('access') refresh = ser.context.get('refresh') username = ser.context.get('username') return Response({'code': 100, 'msg': '成功', 'username': username, 'access': access, 'refresh': refresh}) else: return Response({'code': 101, 'msg': '用户名或密码错误11'})
——context 是视图类和序列化列之间沟通的桥梁(类似与一个空字典)
序列化类配置(serializer.py):
from rest_framework import serializers from .models import User import re from rest_framework.exceptions import ValidationError class LoginSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField() # 写全局钩子 def validate(self, attrs): # 校验用户,签发token username = attrs.get('username') password = attrs.get('password') # 2 使用正则判断用用户名是邮箱,手机号还是用户名,分别去查询当前用户 if re.match(r'^1[3-9][0-9]{9}$', username): user = User.objects.filter(mobile=username).first() elif re.match(r'^.+@.+$', username): user = User.objects.filter(email=username).first() else: user = User.objects.filter(username=username).first() if user and user.check_password(password): # 3 校验密码 # 4 签发token refresh = TokenObtainPairSerializer.get_token(user) self.context['access'] = str(refresh.access_token) self.context['refresh'] = str(refresh) self.context['username'] = user.username return attrs else: raise ValidationError('用户名或密码错误')
进阶版优化(改变返回值):
序列化类配置(serializer.py):
class LoginSerializer(serializers.Serializer): username = serializers.CharField() password = serializers.CharField() # 写全局钩子 def validate(self, attrs): # 校验用户,签发token username = attrs.get('username') password = attrs.get('password') # 2 使用正则判断用用户名是邮箱,手机号还是用户名,分别去查询当前用户 if re.match(r'^1[3-9][0-9]{9}$', username): user = User.objects.filter(mobile=username).first() elif re.match(r'^.+@.+$', username): user = User.objects.filter(email=username).first() else: user = User.objects.filter(username=username).first() if user and user.check_password(password): # 3 校验密码 # 4 签发token refresh = TokenObtainPairSerializer.get_token(user) # 自定义返回值 data = {'code': 100, 'msg': '登录成功成功', 'username': user.username, 'refresh':str(refresh), 'access': str(refresh.access_token) } # 将自定义返回值返回 return data else: raise ValidationError('用户名或密码错误')
——这里将原来返回的attrs换成了自定义的返回格式
视图层配置:
class UserView(GenericAPIView): authentication_classes = () permission_classes = () serializer_class = LoginSerializer def post(self, request): ser = LoginSerializer(data=request.data) if ser.is_valid(): # 会执行字段自己的校验(没有),执行局部钩子(没有),执行全局钩子(写了:校验用户,签发token) # ser.validated_data # 字典,校验过后的数据 return Response(ser.validated_data ) else: return Response({'code': 101, 'msg': '用户名或密码错误'})
——ser.validated_data 表示的是字典,校验过后的数据