首页 > 其他分享 >drf(JWT认证)

drf(JWT认证)

时间:2023-10-10 22:33:14浏览次数:26  
标签:username JWT jwt 认证 token user import payload drf

一. jwt实现过程

1. 构建jwt过程

第一: 用户提交用户名和密码给服务端,如果登录成功,使用jwt创建一个token,并给用户返回

eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InpjYyIsImV4cCI6MTU5NDczODg5MX0.OCG4mUhs_yXIkxtxvG9MWJWjpbvnSGDcqMVtpsn_0mo

第二步: 构建三段字符串之间的关系

# 第一段字符串 headers内部包含了算法 和 token类型。
       流程: 先将python类型对象装换成json格式字符串, 然后做base64加密
    headers = {
        'typ': 'jwt',
        'alg': 'HS256',
    }    

    
# 第二段字符串payload,自定义的值
    流程: 先将python类型对象装换成json格式字符串,然后做base64加密
    payload = {
        'user_id': user.pk,
        'username': username,
        'exp': datetime.datetime.utcnow() + datetime.timedelta(seconds=300),  # 超时时间
    }
    
# 第三段字符串 
    第一步:把1,2部分base64加密过后的结果进行拼接加密
    第二步:对前2部分的加密结果进行hs256加密 + 加盐
    第三步:对hs256加密后的密文在进行base64url加密再拼接到前1, 2部分base64格式的末尾作为sign.

第三步: 以后用户访问时,需要携带token,后端需要对token校验

2. 校验jwt过程

第一步: 获取token

第二步: 对token进行切割, 获取第二段内容进行base64解密,获取payload信息, 检查超时时间是否超时

第三步:由于第三部分的字符串不能反解,把第一和第二段在进行hs256加密

1. 把1,2部分base64的密文拼接加密
2. 对前2部分加密进行hs256加密+加盐得到密文
3. 再将密文机进行base64加密, 与前两段的base64d格式的密文进行对比, 如果相等,表示token没有修改通过.

二. drf-jwt安装

官网: http://getblimp.github.io/django-rest-framework-jwt/

安装: pip install djangorestframework-jwt

三. 使用内置jwt认证+签发token

1. 快速使用

urls.py

# 路由中配置
# 提示: 
'''
obtain_jwt_token本质是由ObtainJSONWebToken类调用as_view类方法实例化出来的, 其实路由中这样写也可以:
    path('login/', ObtainJSONWebToken.as_view()),
'''

from rest_framework_jwt.views import ObtainJSONWebToken, obtain_jwt_token, JSONWebTokenAPIView, VerifyJSONWebToken

urlpatterns = [
    # path('login/', ObtainJSONWebToken.as_view()),
    path('login/', obtain_jwt_token),
]

views.py

from rest_framework_jwt.authentication import JSONWebTokenAuthentication


# Create your views here.
# 快速实现jwt
class BookAPIView(APIView):
    authentication_classes = [JSONWebTokenAuthentication, ]

    def get(self, request):
        return Response('OK')

解析: 为什么路由中配置了obtain_jwt_token用户认证, 签发token等等都不需要写了?

# 帮我们写了视图认证实现接受用户请求及基于请求响应:
    看继承关系: obtain_jwt_token = ObtainJSONWebToken.as_view() -> ObtainJSONWebToken -> JSONWebTokenAPIView
    JSONWebTokenAPIView就是我们的视图类. 它里面写了post方法, 处理我们的认证请求.
    
# 帮我们写了序列化器实现了token的签发:
    class ObtainJSONWebToken(JSONWebTokenAPIView):
        # JSONWebTokenSerializer内部就在序列换器里面使用了validate钩子, 实现了token的签发
        serializer_class = JSONWebTokenSerializer

使用内置提供的认证

头部访问格式: 使用内置的如果没有修改配置文件中配置的前缀, 那么jwt前缀必须要加, 如果不加前缀认证就返回None, 认证就失效了. 大小写都行.

Authorizationjwt eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InlhbmciLCJleHAiOjE1OTUwODY3NDZ9.aaehvGOl3AMI5gfU2Z9L8GH015pWIitOCXLgBJ5zl8E
Authorization JWT eyJ0eXAiOiJqd3QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6InlhbmciLCJleHAiOjE1OTUwODY3NDZ9.aaehvGOl3AMI5gfU2Z9L8GH015pWIitOCXLgBJ5zl8E

拓展: 认证前缀可以修改

from rest_framework_jwt import settings
'JWT_AUTH_HEADER_PREFIX': 'JWT',
rom rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication


# Create your views here.
# 使用jwt提供的认证类,局部使用

# 可以通过认证类JSONWebTokenAuthentication和权限类IsAuthenticated,来控制用户登录以后才能访问某些接口
# 如果用户不登录就可以访问,只需要把权限类IsAuthenticated去掉就可以
class Order(APIView):
    authentication_classes = [JSONWebTokenAuthentication, ]
    permission_classes = [IsAuthenticated, ]

    def get(self, request, *args, **kwargs):
        return Response('这是订单信息')

3 使用内置认证控制登录成功时response返回的格式

utils.py

from rest_framework_jwt.utils import jwt_response_payload_handler


def custom_jwt_response_payload_handler(token, user=None, request=None):
    # 返回什么, 认证成功时就返回什么格式
    return {
        'status': 1000,
        'messages': '登录成功',
        'token': token,
        'username': user.username
    }

settings.py

# 第二步: settings.py文件中配置成自己的路径即可
JWT_AUTH = {
    # utils.jwt_response_payload_handler.custom_jwt_response_payload_handler
    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'utils.jwt_response_payload_handler.custom_jwt_response_payload_handler',
}

四. 自定义jwt认证+签发

1. 自定义jwt认证

1) 继承BaseAuthentication实现

utils.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler  # 用上面的也可以
import jwt
from app01 import models


class MyJwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        jwt_value = request.META.get('HTTP_AUTHORIZATION')
        if jwt_value:
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期!')
            except jwt.DecodeError:
                raise AuthenticationFailed("签名解码错误!")
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('token无效!')
            except Exception as e:
                raise AuthenticationFailed(str(e))
            print(payload)
            # {'user_id': 3, 'username': 'zd', 'exp': 1696772425, 'email': ''}
            # 方式一: 缺点, 查数据库耗费时间
            # user_obj = User.objects.get(pk=payload.get('user_id'))
            # print('user_obj.phone:', user_obj.phone)  # 17621839222

            # 方式二: 缺点, 没有传递的数据就获取不到
            user_obj = models.UserInfo(id=payload.get('user_id'), username=payload.get('username'))
            print('user_obj.phone:', [user_obj.phone])
            return user_obj, jwt_value  # ['']
        # 没有携带值,直接抛异常
        raise AuthenticationFailed('没有携带认证信息')

views.py

from app02.utils import MyJwtAuthentication


class Goods(APIView):
    authentication_classes = [MyJwtAuthentication, ]

    def get(self, request, *args, **kwargs):
        return Response('商品信息')

2) 继承BaseJSONWebTokenAuthentication + 手动get获取jwt_value 或者 自动获取jwt_value实现

utls.py

import jwt
from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication

from rest_framework_jwt.authentication import jwt_decode_handler
from rest_framework_jwt.utils import jwt_decode_handler

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.exceptions import APIException

from rest_framework.authentication import get_authorization_header


class MyJwtAuthentication(BaseJSONWebTokenAuthentication):
    def authenticate(self, request):
        # 手动获取jwt_value
        # jwt_value = request.META.get('HTTP_AUTHORIZATION')
        # 自动获取jwt_value
        jwt_value = get_authorization_header(request)
        if jwt_value:
            try:
                payload = jwt_decode_handler(jwt_value)
            except jwt.ExpiredSignature:
                raise AuthenticationFailed('签名过期!')
            except jwt.DecodeError:
                raise AuthenticationFailed("签名解码错误!")
            except jwt.InvalidTokenError:
                raise AuthenticationFailed('token无效!')
            except Exception as e:
                raise AuthenticationFailed(str(e))
            user_obj = self.authenticate_credentials(payload)
            print('user_obj.phone:', [user_obj.phone])
            return user_obj, jwt_value  # ['']
        # 没有携带值,直接抛异常
        raise AuthenticationFailed('没有携带认证信息')

views.py

from app02.utils import MyJwtAuthentication


class Goods(APIView):
    authentication_classes = [MyJwtAuthentication, ]

    def get(self, request, *args, **kwargs):
        return Response('商品信息')

2. 自定义签发token

# 使用用户名,手机号,邮箱,都可以登录
# 前端需要传的数据格式
{
"username":"lq/13232333333/[email protected]",
"password":'123'
}

1)多方式登录,逻辑写在序列化类中

views.py

from rest_framework.viewsets import ViewSet
from app02.ser import LoginModelSerializer


# class Login1APIView(ViewSetMixin,APIView)
class Login1APIView(ViewSet):  # 跟上面完全一样
    """
    继承ViewSet意义:
        1. 修改视图类中方法, 使用login明确提意
        2. 继承了APIView, 具有较高的可控性
    """

    def login(self, request, *args, **kwargs):
        # 1 需要 有个序列化的类
        login_ser = LoginModelSerializer(data=request.data)
        # 2 生成序列化类对象
        # 3 调用序列化对象的is_validate
        login_ser.is_valid(raise_exception=True)
        token = login_ser.context.get('token')
        username = login_ser.context.get('username')
        return Response({'status': 1000, 'msg': '登录成功', 'token': token, 'username': username})

ser.py

from rest_framework import serializers
import re
from app01 import models
from rest_framework.exceptions import ValidationError
from rest_framework_jwt.utils import jwt_encode_handler, jwt_payload_handler


class LoginModelSerializer(serializers.ModelSerializer):
    # 需要覆盖低下的username字段,数据中是unique,post,认为你保存数据,自己校验不过,不然走不到validate
    username = serializers.CharField()
    class Meta:
        model = models.UserInfo
        fields = ['username', 'password']

    def validate(self, attrs):
        # 在这里写逻辑
        username = attrs.get('username')  # 用户名有三种方式
        password = attrs.get('password')
        # 通过判断,username数据不同,查询字段不一样
        # 正则匹配,如果是手机号
        if re.match(r'^1[3-9][0-9]{9}$', username):
            user_obj = models.UserInfo.objects.filter(phone=username).first()
        elif re.match('^.*?@.*?\.com$', username):
            user_obj = models.UserInfo.objects.filter(email=username).first()
        else:
            user_obj = models.UserInfo.objects.filter(username=username).first()
        if user_obj:  # 用户存在
            # 校验密码,因为是密文,要用check_password
            if user_obj.check_password(password):
                # 签发token
                payload = jwt_payload_handler(user_obj)
                token = jwt_encode_handler(payload)
                self.context['token'] = token
                self.context['username'] = user_obj.username
                return attrs
            else:
                raise ValidationError('密码错误')
        else:
            raise ValidationError('用户不存在')


'''
payload = jwt_payload_handler(user_obj) # 把user传入,得到payload
token = jwt_encode_handler(payload) # 把payload传入,得到token
'''

urls.py

urlpatterns = [
    path('login1/', views.Login1APIView.as_view({'post': 'login'})),
]

2) 多方式登录,逻辑写在视图类中

views.py

# 视图代码
import re
from rest_framework.viewsets import ViewSet
from rest_framework_jwt.utils import jwt_payload_handler
from rest_framework_jwt.utils import jwt_encode_handler
from rest_framework.exceptions import ValidationError

from app01.models import User


class LoginAPIView(ViewSet):
    """
    继承ViewSet意义:
        1. 修改视图类中方法, 使用login明确提意
        2. 继承了APIView, 具有较高的可控性
    """
    def login(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')

        # username=egon/[email protected]/17621839222
        if re.search(r'^1[3-9][0-9]{9}$', username):
            user = User.objects.filter(phone=username).first()
        elif re.search(r'^.*?@.*?qq\.com$', username):
            user = User.objects.filter(email=username).first()
        else:
            user = User.objects.filter(username=username).first()

        if user:
            # 校验密码,因为是密文,要用check_password
            is_login = user.check_password(raw_password=password)
            if is_login:
                # 签发token
                payload = jwt_payload_handler(user)
                token = jwt_encode_handler(payload)
                return Response({'status': 1000, 'token': token, 'results': {'username': user.username, 'email': user.email}})
            raise ('用户密码错误!')
        raise ValidationError("用户名错误!")

五. jwt的配置参数: 过期时间配置

import datetime

JWT_AUTH = {
    # 过期时间七天
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
}

六. base64编码与解码

# md5 固定长度,不可反解
# base63 变长,可反解

# 编码(字符串,json格式字符串)
import base64
import json

dic = {'name': 'lq', 'age': 18, 'sex': '男'}
dic_str = json.dumps(dic)

res = base64.b64encode(dic_str.encode('utf-8'))
print(res)

# 解码
res1 = base64.b64decode(res)
res2 = json.loads(res1)
print(res1, res2)

 

标签:username,JWT,jwt,认证,token,user,import,payload,drf
From: https://www.cnblogs.com/coderxueshan/p/17755927.html

相关文章

  • drf(分页、
    一分页#分页fromrest_framework.paginationimportPageNumberPagination,LimitOffsetPagination,CursorPagination#第一种分页方式:通过指定page获取页数,通过size获取每页显示的条目classCoustomPageNumberPagination(PageNumberPagination):"""url地......
  • drf(序列化练习、user表练习)
    一.APIView版本1.models.pyfromdjango.dbimportmodels#Createyourmodelshere.classCommonField(models.Model):is_delete=models.BooleanField(default=0,verbose_name='True标记被删除的数据,False标记正常使用的数据')create_time=models.DateT......
  • 案例2 网络设备初始化及Console端口密码认证
    1.华为设备<Huawei><Huawei>system-view[Huawei]user-interfaceconsole0[Huawei-ui-console0]authentication-modepassword[Huawei-ui-console0]setauthenticationpasswordcipherqytang[Huawei-ui-console0][Huawei-ui-console0]quit[Huawei][Huawe......
  • 地毯和小地毯16 CFR 1630 和16 CFR 1631表面可燃性标准GCC清关认证
    出口美国地垫GCC清关认证美国联邦法律规定,地毯和垫子要符合易燃性标准和其它要求,包括2008年《美国消费品安全改进法》的要求。在地毯和垫子经过检测或合理检测项目后,作为一般用途的地毯和垫子的生产商和进口商必须在一般合规证书(GCC)中认证,地毯和垫子符合适用标准,确保合规和/或按......
  • 地垫/毛绒地毯出口美国GCC清关认证亚马逊gcc认证
    出口美国地垫GCC清关认证美国联邦法律规定,地毯和垫子要符合易燃性标准和其它要求,包括2008年《美国消费品安全改进法》的要求。在地毯和垫子经过检测或合理检测项目后,作为一般用途的地毯和垫子的生产商和进口商必须在一般合规证书(GCC)中认证,地毯和垫子符合适用标准,确保合规和/或按......
  • BLE常见的无线电认证
    说明​ BLE产品在生产上市前需要通过相关机构认证才能上市。对于无线电法规方面的要求,一般包括蓝牙联盟的认证,和各国无线电法规认证。BQB​ BQB认证是蓝牙的核心认证,任何蓝牙产品上市都必须通过该认证才能认为是符合蓝牙标准的蓝牙产品,相关BQB认证要求可以在蓝牙官网的核心协议......
  • 以实现资源池化弹性管理、企业应用集 中管理、统一安全认证和授权等管理
    使用提供的用户名密码,登录提供的OpenStack私有云平台,在当前租户下,使用CentOS7.9镜像,创建两台云主机,云主机类型使用4vCPU/12G/100G_50G类型。当前租户下默认存在一张网卡,自行创建第二张网卡并连接至controller和compute节点(第二张网卡的网段为192.168.X.0/24,X为工位......
  • 登录认证装饰器
    在FastAPI中,你可以使用装饰器来实现登录认证。以下是一个示例,演示如何创建一个自定义的登录认证装饰器,以确保只有授权的用户可以访问某些接口:fromfastapiimportFastAPI,Depends,HTTPException,statusfromfastapi.securityimportOAuth2PasswordBearer,OAuth2Password......
  • 【实用】登录图形认证 图形码 验证码 中文图形验证码 动态图形验证码 图片验证码 验证
    后端测试: 主要code:https://www.cnblogs.com/liuguiqing/p/17722366.html ......
  • pig4cloud框架系列五:OAuth2之授权码模式认证
    前言:OAuth2目前被广泛用于第三方登录场景中,用于鉴权,认证。本文主要简单介绍一下授权码模式认证。场景:使用微信登录迅雷一,名词概念1,第三方应用程序:迅雷2,服务提供商:微信3,资源所有者:登录用户4,认证服务器:微信用来处理认证的服务器5,资源服务器:微信存放用户生成的资源的服务器,它......