jwt自定义表签发、jwt 多方式登录(auth的user表)

news2024/10/6 0:33:29

补充

# 1 接口文档
	编写规范:
    	-1 描述
        -2 请求地址
        -3 请求方式
        -4 请求参数
        	-headers
            -请求体
            -请求参数
        -5 请求编码格式
        -6 返回格式
        	-示例
            -返回数据字段含义
            
        -其他:
        	-错误状态码
            -...
            
     -接口文档编写位置
    	-写在文件中:word,md,跟前端共享
        -公司有接口文档平台
        	-yapi
            -自研
        -第三方接口文档:coreapi,drf-yasg
        -自动生成---》访问项目地址 的 /doc 路径就能看到接口文档
        
        
 # 2 jwt
	-json  web token的缩写,web方向前后端认证的方式,传统的认证方案使用session,使用jwt后,服务端不需要再存数据了,数据都放在客户端
    -本质原理
    	-jwt 分三段:
        	-头:公司信息,加密方式,声明这是jwt
            -荷载:数据,用户信息,用户id,名字,邮箱,过期时间
            -签名:前面量部分通过某个加密方式加密得到一个签名,base64编码后拼接到最后
    -开发中:
    	签发:登录,登录成功后,按照上面方式生成token串
        认证:客户端携带token到后端,使用原来的加密方式把第一段和第二段再做加密,加密后跟传入的第三代比较,如果一样,说明传入的token可靠,如果不一样,说明数据被篡改,伪造的,不能信任,程序不能继续往后走了
        
----------------------------------------------------------------------------------        
# 3 base64
	-加密算法
    	-1 编码和解码,不加密:base64,url编码和解码
        -2 摘要算法:md5,sha1   不能解开
        -3 对称加密:des,AES     能加密,能解密;加密和解密使用同样的秘钥
        -4 非对称加密 :RSA  能加密,能解密,加密秘钥和解密秘钥是不同的:公钥和私钥
    -编码和解码使用内置模块
----------------------------------------------------------------------------------    
    
    
# 4 django使用jwt
	-django-rest-framework-jwt:有点老
    -djangorestframework-simplejwt:新的
    
    
# 5 快速使用
	-签发:path('login/', obtain_jwt_token),
    -认证(局部使用,全局使用,局部禁用):
    	authentication_classes = [JSONWebTokenAuthentication,]
    	permission_classes = [IsAuthenticated]

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和username
	 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 视图类

## 2.基于auth的user表签发token,做多种方式登录
'''
    用户名+密码   邮箱+密码  手机号+密码  都可以登录
	username+password
    email+password
    phone+password
    
    无论是username,email,phone都以 username形式提交到后端
    于是:从username字段中取出来的,可能是用户名,可能是邮箱,可能是密码---》都能登录成功
'''
from rest_framework.viewsets import ViewSet, GenericViewSet
from rest_framework.decorators import action
import re

from .serializer import LoginSerializer
from .models import AuthUser


class UserView(GenericViewSet):
    # 不需要做序列化或者反序列化,则可以不用写queryset
    # queryset =
    serializer_class = LoginSerializer

    @action(methods=['POST'], detail=False)
    def login(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.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()

        if user and user.check_password(password):
            # 签发
            payload = jwt_payload_handler(user)
            token = jwt_encode_handler(payload)
            return Response({'code': 100, 'msg': '登录成功', 'token': token, 'username': username})

        return Response({'code': 101, 'msg': '用户名或密码错误'})

-------------------优化版----------------------------------------------------
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1002253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL从入门到精通【实践篇】之使用Sharding-JDBC 分库分表详解

文章目录 0. 前言本文技术组件版本基本介绍 2. 使用和配置:步骤1 引入依赖步骤2 配置数据源和分片策略步骤3 核心代码MybatisPlusConfig 核心配置OrderServiceOrderServiceImplOrderInfoOrderMapperOrderControllerBaseMapper 3. 数据库分片配置在我的demo工程中大家…

【2023】基于docker 实现部署jar包项目(包括单个和多个一起部署)

建议学习本博客之前,需要对docke的基本命令有过学习; 目录 前言1、项目打包2、编写Dockerfile文件2.1、单个jar部署🍕Dockerfile文件常用命令 2.2.1、编写一个Dockerfile 文件格式制作镜像 2.1.2、执行docker命令2.2、多个jar一起部署到docke…

电脑提示“Windows Boot Manager boot failed”怎么办?

如果主引导记录(MBR)损坏,则会出现此错误消息“Windows Boot Manager boot failed”。因此,Windows 10引导管理器未能找到操作系统加载程序可能是由于MBR损坏。损坏MBR的原因可能是恶意软件感染或关闭电脑时使用方法不当。那么,Windows提示“…

【iOS】push与present Controller的区别

文章目录 前言一、push方法二、pop方法三、present方法四、dismiss方法五、dismiss多级的方法举例动画 前言 iOS推出与退出界面有两种方式——push与present,接下来笔者分别介绍这两种方式 一、push方法 SecondViewController *second [[SecondViewController all…

运行ORB-SLAM3,整体感觉还不错

安装文档,可以参考 https://blog.csdn.net/u014374826/article/details/132013820 运行测试 双目IMU 可以参考官方文档 Running ROS example: Download a rosbag (e.g. V1_02_medium.bag) from the EuRoC dataset (http://projects.asl.ethz.ch/datasets/doku.ph…

Docker笔记-概念安装简单使用

概念 docker通用词汇。 镜像:Build,创建一个镜像。 仓库:Ship,从仓库和主机上运输镜像。 容器:Run,运行的镜像就是一个容器。 安装 Windows上安装 Docker对win10有原生的支持,win10下的是…

zemax优化功能

1、三种优化方法 zemax的三种优化方法中,局部优化会找到局部的极小值点,全局优化会找到整体的最小值点。 锤形优化适用于先用全局优化找到大概值后,进一步完善光学系统 对于评价函数单调或者局部最小值就是全局最小值的情况,使…

1976~2020年青藏高原典型冰川及冰湖遥感监测数据集

冰川面积是反应气候变化最直接的指标之一。在全球变暖的大背景下,对于评估冰川融化造成的生态、全球气候变化和水资源价值评价等问题十分重要。本文针对受西风和印度洋夏季风影响下的青藏高原冰川及其末端冰湖的变化特征,制作了近44年来时相相对连续的冰…

使用LlamaIndex构建自己的PandasAI

推荐:使用 NSDT场景编辑器 快速搭建3D应用场景 Pandas AI 是一个 Python 库,它利用生成 AI 的强大功能来增强流行的数据分析库 Pandas。只需一个简单的提示,Pandas AI 就可以让你执行复杂的数据清理、分析和可视化,而这以前需要很…

为特征向量数据(1D数组)叠加噪声实现数据增强

为特征向量数据(1D数组)叠加噪声实现数据增强 日期作者版本备注2023.09.11Dog TaoV1.0完成文档的初始版本。 文章目录 为特征向量数据(1D数组)叠加噪声实现数据增强背景介绍叠加噪声的主要方法高斯噪声(Gaussian Nois…

移动机器人(浙大)(待补充)

目录 1.简介 1.1分类 1.2执行模式 2.运动学建模 2.1简述 2.2建模 2.2.1分量叠加 3.导航规划 ​编辑 4.路径规划 1.简介 1.1分类 按移动方式:轮式、履带式、足式、躯干式 1.2执行模式 未知的环境中构建局部地图/已知环境则有地图,根据环境和定位信息进…

AtCoder Beginner Contest 319(D-G)

D.Tasks - AtCoder Beginner Contest 319 (1)题意 给你一个M行得框框和N个单词,每个单词有一个宽度,每个单词之间应该用一个空格隔开,首位单词不需要,问至少需要多宽才能使得单词不会超过M行。 &#xff08…

Batch normalization和Layer normalization

深度学习的归一化方法 1 归一化的目的 当我们使用梯度下降法做优化时,随着网络深度的增加,输入数据的特征分布会不断发生变化,为了保证数据特征分布的稳定性,会加入Normalization。从而可以使用更大的学习率,从而加速…

快速搭建:对象存储平台MinIO

简介:MinIO 是一个高性能的对象存储服务器,兼容Amazon S3云存储服务。适用于大数据存储和用于构建私有云的场景。作为一个对象存储服务,它基于Apache License 开源协议,兼容Amazon S3云存储接口。适合存储非结构化数据&#xff0c…

【软件分析/静态分析】chapter8 课程11/12 指针分析—上下文敏感(Pointer Analysis - Context Sensitivity)

🔗 课程链接:李樾老师和谭天老师的: 南京大学《软件分析》课程11(Pointer Analysis - Context Sensitivity I)_哔哩哔哩_bilibili 南京大学《软件分析》课程12(Pointer Analysis - Context Sensitivity II&…

PCL入门(四):octree简单使用

目录 1. 八叉树(Octree)2. 简单使用 参考博客《三维点云数据的两种结构Kdtree和Octree》和《八叉树》 1. 八叉树(Octree) 只需要考虑三维情况下的八叉树的情况,如下 设置最大的递归深度;找出场景的最大尺寸,并据此创建第一个立方体若未到达…

linux安装jdk1.8

1.下载jdk安装包: https://www.oracle.com/cn/java/technologies/downloads/#java8-windows 2.创建java文件夹: mkdir /usr/local/java3.上传安装包并解压: tar -xzf jdk-xxx.tar.gz4.修改profile文件: vim /etc/profile在文…

计算机专业毕业设计项目推荐04-物业管理系统(SpringBoot+原生Js+Mysql)

物业管理系统(SpringBoot原生JsMysql) **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设计…

物联网 低功耗蓝牙BLE GATT 实现微信小程序通信连接详细教程

蓝牙基本概念 蓝牙技术是一种无线通信的方式,利用特定频率的波段(2.4GHz ~ 2.485GHz左右),进行电磁波传输。蓝牙传输原理是主从关系,一个主设备可以与7个蓝牙从设备配对。 经典蓝牙 vs BLE 蓝牙分为经典蓝牙和低功耗…

最新软件测试面试题+笔试题(十个面试官里有九个会问)

2023最新软件测试面试大全看完offer拿到手软_哔哩哔哩_bilibili2023最新软件测试面试大全看完offer拿到手软共计21条视频,包括:1.HR已读不回问题分析以及如何解决、2.HR已读不回之针对性进行简历优化。、3.HR已读不回之针对性进行技能提升路线。等&#…