drf——基于jwt的多方式登录以及自定义多方式登录
一、基于jwt的多方式登陆
1 手机号+密码 用户名+密码 邮箱+密码
2 流程分析(post请求):
-路由:自动生成(推荐自动生成,自己手写也行)
-视图类:ViewSet(ViewSetMixin, views.APIView)
-序列化类:重写validate方法,在这里面对用户名和密码进行校验
代码实现
models.py----->进行数据迁移
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
phone = models.CharField(max_length=32, unique=True)
settings.py
INSTALLED_APPS = [
...
'rest_framework'
]
#扩写AUTH_USER表
AUTH_USER_MODEL = 'app01.UserInfo'
REST_FRAMEWORK = {
# 配置全局异常
'EXCEPTION_HANDLER': 'app01.utils.common_exception'
}
views.py
from rest_framework.viewsets import ViewSet
from app01.serializer import LoginSerializer
from app01.utils import APIResponse
class LoginViewSet(ViewSet):
def create(self, request, *args, **kwargs):
# 实例化得到一个序列化类的对象
# ser=LoginSerializer(data=request.data,context={'request':request})
ser = LoginSerializer(data=request.data)
# 序列化类的对象的校验方法
ser.is_valid(raise_exception=True) # 字段自己的校验,局部钩子校验,全局钩子校验
# 如果通过,表示登录成功,返回手动签发的token
token = ser.context.get('token')
username = ser.context.get('username')
return APIResponse(token=token, username=username)
# 如果失败,不用管了
serializer.py
from rest_framework import serializers
from app01.models import UserInfo
import re
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler
class LoginSerializer(serializers.ModelSerializer):
#重写username不然报错
username = serializers.CharField()
class Meta:
model = UserInfo
fields = ['username', 'password']
def validate(self, attrs):
# username可能是邮箱,手机号,用户名
username = attrs.get('username')
password = attrs.get('password')
# 如果是手机号
if re.match('^1[3-9]\d{9}$', username):
# 以手机号登录
user = UserInfo.objects.filter(phone=username).first()
elif re.match('^.+@.+$', username):
# 以邮箱登录
user = UserInfo.objects.filter(email=username).first()
else:
# 以用户名登录
user = UserInfo.objects.filter(username=username).first()
# 如果user有值并且密码正确
if user and user.check_password(password):
# 登录成功,生成token
# drf-jwt中有通过user对象生成token的方法
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
# token是要在视图类中使用,现在我们在序列化类中
# self.context.get('request')
# 视图类和序列化类之间通过context这个字典来传递数据
self.context['token'] = token
self.context['username'] = user.username
#一定要记得return
return attrs
else:
raise ValidationError('用户名或密码错误')
utils.py
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, code=100, msg='成功', data=None, status=None,
headers=None, content_type=None, **kwargs):
dic = {'code': code, 'msg': msg}
if data:
dic['data'] = data
dic.update(kwargs)
super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
from rest_framework.views import exception_handler
#全局异常捕获
def common_exception(exc, context):
# 先调用REST framework默认的异常处理方法获得标准错误响应对象
response = exception_handler(exc, context)
# 在此处补充自定义的异常处理
if response is None:
response = Response(data={'code':999,'msg':str(exc)})
return response
urls.py
注意:自动生成路由,四种对应关系

from django.urls import path
from rest_framework.routers import SimpleRouter
from app01 import views
router = SimpleRouter()
#必须要加,basename='login',不然会报错
router.register('login', views.LoginViewSet,basename='login')
print(router.urls)
urlpatterns = [
... #path('login/', views.LoginViewSet.as_view({'post':'create'})), 可以用这种自己手写的路由
]
urlpatterns += router.urls
登录方式:在http://127.0.0.1:8000/login/发送post请求,携带json格式username,password
二、自定义user表,签发token,认证类的代码实现多方式登录
models.py
from django.db import models
class MyUser(models.Model):
username = models.CharField(max_length=32) #字段名一定要叫username不然要自己重写,具体看源码
password = models.CharField(max_length=32)
phone = models.CharField(max_length=32)
email = models.EmailField()
utils.py
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, code=100, msg='成功', data=None, status=None,
headers=None, content_type=None, **kwargs):
dic = {'code': code, 'msg': msg}
if data:
dic['data'] = data
dic.update(kwargs)
super().__init__(data=dic, status=status, headers=headers, content_type=content_type)
from rest_framework.views import exception_handler
#全局异常捕获
def common_exception(exc, context):
# 先调用REST framework默认的异常处理方法获得标准错误响应对象
response = exception_handler(exc, context)
# 在此处补充自定义的异常处理
if response is None:
response = Response(data={'code':999,'msg':str(exc)})
return response
views.py
from rest_framework.views import APIView
from app01.utils import APIResponse
import re
from app01.models import MyUser
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
from rest_framework_jwt.views import obtain_jwt_token
class MyLoginView(APIView):
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
# 如果是手机号
if re.match('^1[3-9]\d{9}$', username):
# 以手机号登录
user = MyUser.objects.filter(phone=username).first()
elif re.match('^.+@.+$', username):
# 以邮箱登录
user = MyUser.objects.filter(email=username).first()
else:
# 以用户名登录
user = MyUser.objects.filter(username=username).first()
# 如果user有值并且密码正确,注意这里user.password == password
if user and user.password == password:
# 登录成功,生成token
# drf-jwt中有通过user对象生成token的方法
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
return APIResponse(token=token, username=user.username)
else:
return APIResponse(code=101, msg='用户名或密码错误')
urls.py
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('login2/', views.MyLoginView.as_view()),
]
settings.py
INSTALLED_APPS = [
...
'rest_framework'
]
REST_FRAMEWORK = {
# 配置全局异常
'EXCEPTION_HANDLER': 'app01.utils.common_exception'
}
在自定义登录的基础上,加上自定义的认证,来查询订单信息
auth.py
from rest_framework_jwt.utils import jwt_decode_handler
import jwt
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from app01.models import MyUser
class JwtAuthentication(BaseJSONWebTokenAuthentication):
def authenticate(self, request):
token=request.META.get('HTTP_Authorization'.upper())
try:
payload = jwt_decode_handler(token)
except jwt.ExpiredSignature:
raise AuthenticationFailed('过期了')
except jwt.DecodeError:
raise AuthenticationFailed('解码错误')
except jwt.InvalidTokenError:
raise AuthenticationFailed('不合法的token')
# 得到的user对象,应该是自己user表的user对象
print(payload)
# user=MyUser.objects.get(id=payload['user_id']) 这样写不好,会每次都查一次数据库
user=payload #不用每次查数据库#或者user = MyUser(id=payload["user_id"], username = payload["username"])不用每次查数据库
return (user, token)
views.py 加上以下认证代码
from app01.auth import JwtAuthentication
class OrderAPIView(APIView):
authentication_classes = [JwtAuthentication, ]
def get(self, request):
# print(request.user) # 自己的user对象
print(request.user) # user是个字典,内部有user_id,
# 后续要查询该用户的所有订单,直接根据user_id查询即可
return APIResponse(msg='查询订单成功')
urls.py
urlpatterns = [
path('order/', views.OrderAPIView.as_view()),
]
赞 (0)
