首页 > 其他分享 >drf-jwt自定义表签发、多方式登录

drf-jwt自定义表签发、多方式登录

时间:2023-09-11 19:22:41浏览次数:53  
标签:username 自定义 jwt token user import password drf

一、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

相关文章

  • jwt自定义表签发、jwt多方式登录(auth的user表)
    jwt自定义表签发models.pyfromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportAbstractUser#继承AbstractUser直接使用自动签发token#纯自己写的用户表,需要自己签发classUser(models.Model):username=models.CharField(max_length=32)......
  • jwt自定义表签发, jwt 多方式登录(auth的user表)
    1jwt自定义表签发1.1models.pyfromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportAbstractUser#继承AbstractUser直接使用自动签发token#纯自己写的用户表,需要自己签发classUser(models.Model):username=models.CharField(max_leng......
  • drf-day11
    jwt自定义表签发models.pyfromdjango.dbimportmodelsfromdjango.contrib.auth.modelsimportAbstractUser#继承AbstractUser直接使用自动签发token#纯自己写的用户表,需要自己签发classUser(models.Model):username=models.CharField(max_length=32)......
  • Vue2x的自定义指令
    在某些情况下,我们需要对底层DOM进行操作,而内置的指令不能满足需求,就需要自定义指令。一个自定义指令由一个包含类似组件的生命周期的钩子的对象来定义,钩子函数会接收到指令所绑定的元素作为参数。定义指令常用两种方式进行自定义指令,一种是全局定义,另一种在当前组件中定义//局......
  • SpringBoot + 自定义注解,实现用户操作日志(支持SpEL表达式)
    背景一个成熟的系统,都会针对一些关键的操作,去创建用户操作日志。比如:XX人创建了一条订单,订单号:XXXXXXXXX因为操作人或者订单号是动态的,所以有些开发人员,不知道获取,就将这种操作日志和业务代码融在一起。我们当然要杜绝这种现象,一定会有更好的解决方案。当前项目除了......
  • MySQL数据库进阶 自定义函数
    自定义函数在MySQL中,您可以使用自定义函数来扩展数据库管理系统的功能。自定义函数允许您封装一段可重用的代码,并在查询和其他操作中调用它。以下是在MySQL中创建和使用自定义函数的一般步骤:1、创建自定义函数语法:CREATEFUNCTIONfunction_name(parameters)RETURNSreturn_t......
  • drf - 过滤、排序、异常源码剖析、jwt
    过滤类的源码剖析1、为什么在视图类中配置了一个过滤类,就可以走? -filter_backends=[SearchFilter,MyFilter]2、前提条件是必须继承在视图类中继承GenericAPIView: 因为filter_backends是GenericAPIView的类属性。3、如果光继承了GenericAPIView还是不行,还需要再继承List......
  • drf- 过滤、排序、异常处理
    session的执行流程写一个登录接口----->保存用户的登录状态 -获取到用户名,密码-使用request.session["username"]=用户名、或者request.session["pk"]=pk值-签发阶段做了三件事: -1、生成一个随机的字符串-2、在django_session表中保存 -se......
  • drf- 三大认证、排序
    三大认证组件登录认证我们可以使用auth自带的User表,也可以选择自定义表.写登录接口,登录成功要有标志,生成一个随机的字符串,放到表中,以后只要携带这个字符串就可以登录成功。视图层方法一:classUserView(GenericViewSet):queryset=models.UserInfo.objects.all()......
  • 登录拦截器校验JWT
    importcom.alibaba.csp.sentinel.util.StringUtil;importcom.xtw.enums.BizCodeEnum;importcom.xtw.model.LoginUser;importcom.xtw.util.CommonUtil;importcom.xtw.util.JWTUtil;importcom.xtw.util.JsonData;importio.jsonwebtoken.Claims;importorg.springf......