通过drf和jwt实现多方式登录

发布时间 2024-01-03 21:16:24作者: wellplayed

需求如下:

用户输入:用户名或手机号或邮箱 +密码都能登录,并签发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': self.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 表示的是字典,校验过后的数据