1 jwt自定义表签发
1.1 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, '未知')))
1.2 视图
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': '用户名或密码错误'})
1.3 路由
path('login/', UserView.as_view()),
2 jwt 多方式登录(auth的user表)
#1 用户名+密码 邮箱+密码 手机号+密码 都可以登录
username+password
email+password
phone+password
无论是username,email,phone都以 username形式提交到后端
于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功
#2 auth的user签发
#3 签发,校验用户 逻辑-----》放在序列化类中
# 5 存在一个问题---》已经迁移过表了---》已经存在auth的user表了,如果再去继承AbstractUser,再写用户表,就会出错
-解决方案:以后尽量不要这么做
-以后要扩写auth的user表,一开始就要扩写,不要等迁移完之后再扩写
-删库
-删迁移文件(不要删__init__.py和migrations文件夹)
-项目app的迁移文件
-django内置app的admin和auth的迁移文件
-重新迁移--两条命令
-扩写auth的user表,需要在配置文件配置 ###重要
# 6 创建超级用户--->创建到扩写的表 auth的user--》AuthUser
python manage.py createsuperuser
2.1 总结流程
# 序列化,反序列化,数据校验---》只用来做数据校验
# 前端传过来的字段,都要,而且要校验 :username password
# 只要视图类中执行 ser.is_valid():
会走字段自己的规则---》username过不了---》因为有unique---》所有需要重写
会走局部钩子---》咱们没写
会走全局钩子---》全局钩子里校验
-分成了两个方法:好处是以后修改方法
-_get_user :多方式的 ,以后改成单方式登录,只要该这个方法即可
-_get_token:用的第三方签发,后期改成自己的签发,只需要改它即可
-把生成的token和用户名放到了,序列化类中,单是怕污染数据,放到了序列化类的对象的context中
# 视图类中取出来
token = ser.context.get('token')
username = ser.context.get('username')
2.2 序列化类
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
2.3 视图类
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':'用户名密码错误'})
2.4 路由
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,自定义,jwt,auth,---,token,user,import
From: https://www.cnblogs.com/huangchunfang/p/17693540.html