一、jwt自定义表签发
自定义表签发,用的是自己定义的表
1. models.py:
- 注意点:因为视图中使用了drf-jwt的自动签发,所以用户名必须为username
from django.db import models # 自定义签发用的是自定义的user表 # 注意点:使用drf-jwt的自动签发,用户名必须是username class User(models.Model): username = models.CharField(max_length=32) password = models.CharField(max_length=32) email = models.EmailField(max_length=32) phone = models.CharField(max_length=32) gender = models.IntegerField(choices=((1, '男'), (2, '女'), (0, '未知')))
2. views.py:
from rest_framework.views import APIView 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 from .models import User # 自定义签发表签发 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签发 # 通过user生成payload,jwt中有提供一个方法: payload = jwt_payload_handler(user) # jwt提供的方法:通过payload生成token token = jwt_encode_handler(payload) return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username}) else: return Response({'code': 101, 'msg': '用户名或者密码错误'})
drf配置文件:
3. urls.py:
from django.contrib import admin from django.urls import path from app01.views import UserView urlpatterns = [ path('admin/', admin.site.urls), path('login/',UserView.as_view()), # 登录对应的路由 ]
4. postman测试结果:
- 发送POST请求,请求地址:http://127.0.0.1:8000/login/,在请求体输入用户名和密码
二、 jwt多方式登录(auth的user表)
1.登录方式:
- 用户名+密码
- 邮箱+密码
- 手机号+密码
这几种方式都可以登录,无论是username,email,phone都以usernam的形式提交到后端。
于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码,都能登录成功
2. auth的user签发
3. 签发,校验用户的逻辑,放在序列化类中
这个序列化类只用来进行对数据的校验(反序列化校验)
4. 存在一个问题:
当已经迁移过表了,就已经存在auth_user表了,如果在去继承AbstractUser,在写用户表,就会出错
- 解决方案:以后尽量不要这么做
- 以后扩写auth的User表,在迁移表之前进行扩写
- 删库
- 删迁移文件(不要删__init__.py和migrations文件夹)
- 项目app的迁移文件
- django内置app的admin和auth的迁移文件migrations文件中的记录,除了init
- 重新迁移--两条命令
- 扩写auth的user表,需要在配置文件配置
5. 创建超级用户 :创建到扩写的表(authuser表)
- 创建超级用户:python manage.py createsuperuser
多方式登录的流程:
序列化类:只用来做数据校验
- 前端传过来的字段都要校验:username、password
视图类中执行 ser.is_valid():
-首先会走字段自己的规则,username过不了,因为有unique:它是从表中映射过来的,只要校验,就会去表中查有没有和前端提交的username一样的,所以要重写username字段
- 会走局部钩子(没有写)
- 会走全局钩子,在全局钩子中进行校验
- 写了两个方法:好处是以后有什么变化,只需要修改方法
- _get_user:多方式登录校验,以后改成单方式登录,只要修改这个方法即可
在类的内部,用__开头表示隐藏,我们约定俗成,以_开头,表示只在类内部使用,不给外部使用,但是万一外部要用,直接用就可以了
- _get_token:用的第三方签发,后期改成自己的签发,我们只需要修改这个方法即可
- 把生成的token和用户名放到了序列化类中,但是怕污染数据,放到了序列化类的对象的context中
- 从视图类中取出来
token = ser.context.get('token')
username = ser.context.get('username')
1. 多方式登录签发的简单方法:
models.py:
from django.contrib.auth.models import AbstractUser class AuthUser(AbstractUser): # 默认有:username,password,email,。。。。 # 扩写字段 phone = models.CharField(max_length=32)
views.py:
# 基于auth的user表,多方式登录签发---简单方式 # 继承GenericViewSet,自己签发token,做多方式登录##### 写序列化类 from rest_framework.viewsets import GenericViewSet from .serializer import LoginSerializer from rest_framework.response import Response from rest_framework.decorators import action import re from .models import AuthUser class UserView(GenericViewSet): serializer_class = LoginSerializer @action(methods=['POST'], detail=False) def login(self, requset,*args,**kwargs): username = requset.data.get('username') password = requset.data.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() # 查询用户名和密码是否正确 # 不能直接拿明文密码和密文密码进行比较,所以用check_password来比较 if user and user.check_password(password): # 校验用过,签发token payload = jwt_payload_handler(user) token = jwt_encode_handler(payload) return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': user.username}) else: return Response({'code': 101, 'msg': '用户名或者密码错误'})
urls.py:
from django.contrib import admin from django.urls import path,include from app01.views import UserView from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('user',UserView,'user') urlpatterns = [ path('admin/', admin.site.urls), path('',include(router.urls)) # 多方式登录签发路由 ]
serializer.py:
from rest_framework import serializers from .models import AuthUser class LoginSerializer(serializers.ModelSerializer): class Meta: model = AuthUser fields = ['username','password']
postman测试结果:
- 请求方式:POST,请求地址:http://127.0.0.1:8000/user/login/
- 用户名+密码
- 邮箱+密码:
2. 多方式登录签发的复杂方式
将用户登录方式的校验放在序列化类中进行校验
models.py:
from django.contrib.auth.models import AbstractUser class AuthUser(AbstractUser): # 默认有:username,password,email,。。。。 # 扩写字段 phone = models.CharField(max_length=32)
views.py:
# 基于auth的user表,多方式登录签发---复杂方式 from rest_framework.viewsets import GenericViewSet from .serializer import LoginSerializer from rest_framework.response import Response from rest_framework.decorators import action class UserView(GenericViewSet): serializer_class = LoginSerializer @action(methods=['POST'],detail=False) def login(self,requset,*args,**kwargs): # 从前端获取提交的数据,进行序列化得到序列化对象 ser = self.get_serializer(data=requset.data) if ser.is_valid(): # 进行反序列化校验 # 校验通过则取出token,和username username = ser.context.get('username') token = ser.context.get('token') return Response({'code':100,'msg':'登录成功','token':token,'username':username}) else: return Response({'code':101,'msg':'用户名或者密码错误'})
serializer.py:
from rest_framework import serializers from rest_framework.exceptions import ValidationError from rest_framework_jwt.settings import api_settings # rest_framework_jwt.utils.jwt_encode_handler jwt_encode_handler= api_settings.JWT_ENCODE_HANDLER # rest_framework_jwt.utils.jwt_payload_handler jwt_payload_handler=api_settings.JWT_PAYLOAD_HANDLER from .models import AuthUser import re class LoginSerializer(serializers.ModelSerializer): # 因为Username是直接从auth的user表中映射过来的, # username 有自己的字段规则:unique=True ,当前端提交的username和数据库中的一样时,就会报错 # 因此要重写这个username字段 username = serializers.CharField() class Meta: model = AuthUser fields = ['username','password'] # 判断是哪个登录的方式 def _get_user(self,attrs): # attrs 是前端传入,校验过后的数据 {"username": "hh","password": "123" } username = attrs.get('username') password = attrs.get('password') if re.match(r'^1[3-9][0-9]{9}$',username): # 说明是phone+password的登录方式 user = AuthUser.objects.filter(phone=username).first() elif re.match(r'^.+@.+$',username): # 说明是email+password的登录方式 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: #如果校验没有通过,就直接抛出异常 raise ValidationError('用户名或者密码错误') # 校验通过就签发token def _get_token(self,user): payload =jwt_payload_handler(user) token = jwt_encode_handler(payload) return token # 全局钩子 def validate(self, attrs): user = self._get_user(attrs) # 如果有user 说明用户名和密码通过验证 token = self._get_token(user) # 用户名和密码验证通过,则签发token # 将username和token放入ser中,通过context天骄,context是空字典 self.context['username']=user.username self.context['token']=token return attrs
urls.py:
from django.contrib import admin from django.urls import path,include from app01.views import UserView from rest_framework.routers import SimpleRouter router = SimpleRouter() router.register('user',UserView,'user') urlpatterns = [ path('admin/', admin.site.urls), path('',include(router.urls)) # 多方式登录签发路由 ]
postman测试:
- 请求方式:POST,请求地址:http://127.0.0.1:8000/user/login/
- 邮箱+密码:
- 用户名+密码:
标签:username,自定义,jwt,token,user,import,password,drf From: https://www.cnblogs.com/Lucky-Hua/p/17694286.html