jwt自定义表签发
models.py
from django.db import models from django.contrib.auth.models import AbstractUser # 继承AbstractUser 直接使用自动签发token # 纯自己写的用户表,需要自己签发 class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) email = models.EmailField(max_length=32) gender = models.IntegerField(choices=((1, '男'), (2, '女'), (0, '未知')))
视图类
from django.shortcuts import render from rest_framework.views import APIView from .models import User from rest_framework.response import Response from rest_framework_jwt.settings import api_settings # drf的配置文件 # from rest_framework_jwt.utils import jwt_payload_handler jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER # rest_framework_jwt.utils.jwt_encode_handler jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER class UserView(APIView): def post(self, request): username = request.data.get('username') password = request.data.get('password') user = User.objects.filter(username=username, password=password).first() if user: # 签发token # 1 通过user生成payload---》jwt 提供一个方法(username),传入user,返回payload # payload = jwt_payload_handler(user) payload={'username':'asdfasdf','exp':1694401763} # 2 生成token---》jwt提供了方法,把payload放入--》token token = jwt_encode_handler(payload) return Response({'code': 101, 'msg': '登录成功', 'token': token, 'username': user.username}) else: return Response({'code': 101, 'msg': '用户名或密码错误'})
路由
path('login/', UserView.as_view())
jwt 多方式登录(authuser表)
1 用户名+密码 邮箱+密码 手机号+密码 都可以登录 username+password email+password phone+password 无论是username,email,phone都以 username形式提交到后端 于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功 2 auth的user签发 3 签发,校验用户 逻辑-----》放在序列化类中 4 存在一个问题---》已经迁移过表了---》已经存在auth的user表了,如果再去继承AbstractUser,再写用户表,就会出错 -解决方案:以后尽量不要这么做 -以后要扩写auth的user表,一开始就要扩写,不要等迁移完之后再扩写 删库 删迁移文件(不要删__init__.py和migrations文件夹) 项目app的迁移文件 django内置app的admin和auth的迁移文件 重新迁移--两条命令 扩写auth的user表,需要在配置文件配置 ###重要 5 创建超级用户--->创建到扩写的表 auth的user--》AuthUser python manage.py createsuperuser
序列化类
from .models import AuthUser from rest_framework import serializers import re from rest_framework.exceptions import ValidationError from rest_framework_jwt.settings import api_settings # drf的配置文件 # from rest_framework_jwt.utils import jwt_payload_handler jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER # rest_framework_jwt.utils.jwt_encode_handler jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER # 序列化类干什么? 只用来反序列化的校验 class LoginSerializer(serializers.ModelSerializer): # username 是表中的字段---》自动映射过来的-->A user with that username already exists # username有字段自己的规则---》唯一 unique---》去数据库查询发现有lqz,之间字段自己规则报错了,不会走到全局钩子 username=serializers.CharField() # 需要重写字段,不重写,字段自己规则过不了 class Meta: model = AuthUser # 千万不要加逗号,程序运行不会报错,使用这个地方的时候,就报错了 fields = ['username', 'password'] # 在类内部 用 __ 开头表示隐藏 # 我们约定俗称,以 _ 开头的,表示只在类内部使用,不给外部用,但是万一外部要用,之间用既可以了 def _get_user(self, attrs): # 用户名从哪拿? attrs是前端传入,校验过后的数据 {"username": "lqz","password": "lqz12345" } username = attrs.get('username') password = attrs.get('password') if re.match(r'^1[3-9][0-9]{9}$', username): # 说明,用户提交的是手机号+密码 user = AuthUser.objects.filter(phone=username).first() elif re.match(r'^.+@.+$', username): # (这个邮箱正则,不太准确) 如果是邮箱,说明用户提交的是 邮箱+密码 user = AuthUser.objects.filter(email=username).first() else: user = AuthUser.objects.filter(username=username).first() if user and user.check_password(password): return user else: # 在全局钩子中,只要校验不通过,就抛异常 # 不是return raise ValidationError('用户名或密码错误') def _get_token(self, user): payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return token def validate(self, attrs): # 1 校验用户 user = self._get_user(attrs) # 如果返回user,说明,用户名密码对了,如果没走到这里,说明抛异常,抛异常说明用户名密码错误 # 2签发token token = self._get_token(user) # 3 放在这里面 self 是序列化类的对象 ,context 是空字典,它是 视图类和序列化类之间沟通的桥梁 self.context['token'] = token self.context['username'] = user.username # 4 返回校验过后的数据 return attrs
视图类
from rest_framework_jwt.views import obtain_jwt_token ## 复杂方式---》校验逻辑--》放在序列化类中 class UserView(GenericViewSet): # queryset = 可以不写 serializer_class = LoginSerializer @action(methods=['POST'], detail=False) def login(self, request, *args, **kwargs): # 拿到前端传入的用户名和密码,得到一个序列化类对象 ser = self.get_serializer(data=request.data) if ser.is_valid(): # 字段自己(校验不过,因为username在数据库中有你传入的这个名字了),局部钩子,全局钩子---》需要在序列化类中写 # 校验完,并且签发完token了 # token从 序列化类的对象 取出来 # username从 序列化类的对象 取出来 # 现在不明白如何取出来的,假设取出来是对的---》因为在全局钩子中放入到了context中 token = ser.context.get('token') username = ser.context.get('username') return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': username}) else: return Response({'code': 101, 'msg':'用户名密码错误'})
路由
from django.contrib import admin from django.urls import path from app01.views import UserView from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('user', UserView, 'user') # 127.0.0.1:8000/user/login 的post请求 urlpatterns = [ path('admin/', admin.site.urls), # path('login/', UserView.as_view()), ] urlpatterns += router.urls
标签:username,password,jwt,token,user,import,drf,day11 From: https://www.cnblogs.com/shanghaipudong/p/17693512.html