views:
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from .serializer import LoginSerializer
class LoginViewSet(ViewSet):
# 需要和mixins结合使用,继承GenericViewSet,不需要则继承ViewSet
# 为什么继承视图集,不去继承工具视图或视图基类,因为视图集可以自定义路由映射:
# 可以做到get映射get,get映射list,还可以做到自定义(灵活)
def login(self, request, *args, **kwargs):
serializer = LoginSerializer(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
token = serializer.context.get('token')
return Response({'code': 100, 'msg': '登录成功', "token": token})
serializer:
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
from .models import User
# 重点:自定义login,完成多方式登录
class LoginSerializer(serializers.ModelSerializer):
# 登录请求,走的是post方法,默认post方法完成的是create入库校验,所以唯一约束的字段,会进行数据库唯一校验,导致逻辑相悖
# 需要覆盖系统字段,自定义校验规则,就可以避免完成多余的不必要校验,如唯一字段校验
username = serializers.CharField()
class Meta:
model = User
# 结合前台登录布局:采用账号密码登录,或手机密码登录,布局一致,所以不管账号还是手机号,都用username字段提交的
fields = ('username', 'password')
def validate(self, attrs):
# 在全局钩子中,才能提供提供的所需数据,整体校验得到user
# 再就可以调用签发token算法,将user信息转换为token
# 将token存放到context属性中,传给外键视图类使用
user = self._get_user(attrs)
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
self.context['token'] = token
return attrs
# 多方式登录
def _get_user(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
import re
if re.match(r'^1[3-9][0-9]{9}$', username):
# 手机登录
user = User.objects.filter(phone=username, is_active=True).first()
elif re.match(r'^.+@.+$', username):
# 邮箱登录
user = User.objects.filter(email=username, is_active=True).first()
else:
# 账号登录
user = User.objects.filter(username=username, is_active=True).first()
if user and user.check_password(password):
return user
raise ValidationError({'user': 'user error'})
utils:
import re
from .models import User
from django.contrib.auth.backends import ModelBackend
class JWTModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
if re.match(r'^1[3-9]\d{9}$', username):
user = User.objects.get(mobile=username)
else:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
if user.check_password(password) and self.user_can_authenticate(user):
return user
urls:
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('login/', views.LoginViewSet.as_view({'post':'login'}))
]