多方式登录

路由

# 127.0.0.1:8000/app01/api/v1/jwt/login/--->post请求
router.register('jwt', UserJWTView, 'jwt')
urlpatterns = [
    # # 方案一: 前面可以再加前缀
    path('api/v1/', include(router.urls)),
    path('users/', UserView.as_view()),
    path('login/', token_obtain_pair), 
]

视图类

from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
from .serializer import LoginJWTSerializer
class UserJWTView(GenericViewSet):
    serializer_class = LoginJWTSerializer
    @action(methods=['POST'],detail=False)
    def login(self,request,*args,**kwargs):
        # 正常逻辑:取出手机号/用户名/邮箱+密码--》去数据校验--》校验通过-->签发token--》返回给前端
        # 高级逻辑:使用序列化类做上述逻辑
        serializer=self.get_serializer(data=request.data)
        if serializer.is_valid(): # 执行 三层认证
            # 校验通过:会把user,access和refresh都放到序列化类对象中--》返回给前端、
            # 现在在视图类中----》有个序列化类--》把视图类中变量给序列化类---》序列化类的变量给视图类--》借助于context给[字典]
            refresh = serializer.context.get('refresh')
            access = serializer.context.get('access')
            return Response({'code': 100, 'msg': '登录成功', 'refresh': refresh, 'access': access})
        else:
            return Response({'code': 101, 'msg': serializer.errors})

序列化类

from rest_framework import serializers
import re
from .models import UserInfo
from rest_framework.exceptions import ValidationError
class LoginJWTSerializer(serializers.Serializer):
    username = serializers.CharField() # 可能是 用户名  手机号  邮箱
    password=serializers.CharField()
    def _get_user(self,attrs):
        # 1 校验用户
        username = attrs.get('username')
        password = attrs.get('password')
        # 2 去数据库 查询用户---》username可能是手机号,邮箱,用户名--》查的字段不一样
        # 2.1 正则匹配,是不是手机号
        if re.match(r'^1[3-9][0-9]{9}$', username):
            user = UserInfo.objects.filter(mobile=username).first()
        elif re.match('^.+@.+$', username):
            user = UserInfo.objects.filter(email=username).first()
        else:
            user = UserInfo.objects.filter(username=username).first()

        if user and user.check_password(password):
            return user
        else:
            raise ValidationError('用户名或密码错误')
    def validate(self, attrs):
        # 取出 手机号/用户名/邮箱+密码--》数据库校验--》校验通过签发 access和refresh,放到context中
        user=self._get_user(attrs)
        # 签发token--》通过user对象,签发token
        token = RefreshToken.for_user(user)
        self.context['access'] = str(token.access_token)
        self.context['refresh'] = str(token)
        return attrs  # 不返回不行:因为源码中校验了是否为空--》

总结

# 1 校验数据,放到序列化类的 validate中,而不放在视图类的方法中乐
# 2 视图类和序列化类直接交互变量
	serializer.context
    
# 3 user.check_password  必须是auth的user表,校验密码使用它


# 4 attrs必须返回值,返回空报错

# 5 视图类的方法校验失败的else中:也要return Response
# 6 如何签发token
token = RefreshToken.for_user(user)
self.context['access'] = str(token.access_token)
self.context['refresh'] = str(token)

2自定义用户表

签发roken

不扩写auth
只要重写def _get_user的逻辑就可以了

2.1 新建用户表

class User(models.Model):
    username = models.CharField(max_length=32)
    password = models.CharField(max_length=32)
    user_type=models.IntegerField(choices=((1,'注册用户'),(2,'普通管理员'),(3,'超级管理员')),default=1)
    @property
    def is_authenticated(self):
        return True

2.2 登陆签发

2.2.1 路由

# 127.0.0.1:8000/app01/api/v1/our_jwt/login/--->post请求
router.register('our_jwt', UserOurJWTView, 'our_jwt')
# 127.0.0.1:8000/app01/api/v1/publish/--->get
router.register('publish', PublishView, 'publish')

2.2.2 视图类

class UserOurJWTView(GenericViewSet):
    serializer_class = LoginOurJWTSerializer
    @action(methods=['POST'],detail=False)
    def login(self,request,*args,**kwargs):
        serializer=self.get_serializer(data=request.data)
        if serializer.is_valid():
            refresh = serializer.context.get('refresh')
            access = serializer.context.get('access')
            return Response({'code': 100, 'msg': '登录成功', 'refresh': refresh, 'access': access})
        else:
            return Response({'code': 101, 'msg': serializer.errors})
        

2.2.3 序列化类

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.tokens import RefreshToken
from .models import User
class LoginOurJWTSerializer(serializers.Serializer):
    username = serializers.CharField() # 可能是 用户名  手机号  邮箱
    password=serializers.CharField()
    def _get_user(self,attrs):
        # 1 校验用户
        username = attrs.get('username')
        password = attrs.get('password')
        user=User.objects.filter(username=username,password=password).first()

        if user:
            return user
        else:
            raise ValidationError('用户名或密码错误')
    def validate(self, attrs):
        user=self._get_user(attrs)
        token = RefreshToken.for_user(user)
        self.context['access'] = str(token.access_token)
        self.context['refresh'] = str(token)
        return attrs

2.3 认证类

2.3.1 认证类

from rest_framework_simplejwt.authentication import JWTAuthentication
from .models import User
class JWTOurAuth(JWTAuthentication):
    def authenticate(self, request):
        # 取出用户携带的access---》放请求头中:Authorization
        token = request.META.get('HTTP_AUTHORIZATION')
        if token:
            # 校验token--》validated_token 返回的就是可以信任的payload
            validated_token = self.get_validated_token(token)
            user_id = validated_token['user_id']
            user = User.objects.filter(pk=user_id).first()
            return user, token
        else:
            raise AuthenticationFailed('请携带登录信息')

2.3.2 使用

# 登陆后才能访问
from .authentication import JWTOurAuth
class PublishView(GenericViewSet):
    authentication_classes = [JWTOurAuth]
    def list(self,request):
        return Response('get')

另一个思路的多方式登录

#settings.py
# 自定义验证, 通过手机号,邮件登陆系统
AUTHENTICATION_BACKENDS = (
    'utils.backends.MobileOrEmailBackend',
    # 'django.contrib.auth.backends.ModelBackend',
)
#backends.py
from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

from apps.account.models import User


class MobileOrEmailBackend(ModelBackend):
    def authenticate(self, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(username=username) | Q(email=username) | Q(mobile=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None



posted @ 2024-04-19 19:35  沉岩  阅读(3)  评论(0编辑  收藏  举报