rest_framework_django学习笔记三(异常、登录认证)

rest_framework_django学习笔记三(异常、登录认证)

一、异常

REST framework 定义的异常

异常名称说明
APIException所有异常的父类
ParseError解析错误
AuthenticationFailed认证失败
NotAuthenticated尚未认证
PermissionDenied权限拒绝
NotFound未找到
MethodNotAllowed请求方式不支持
NotAcceptable要获取的数据格式不支持
Throttled超过限流次数
ValidationError校验失败

1、自定义异常

REST framework 提供了异常处理,我们可以自定义异常处理函数

from  rest_framework.views import exception_handler
def custom_exception_handler(exc,context):
	# 先调用 REST framework 默认的异常处理方法获得标准错误响应对象
	response = exception_handler(exc,context)
	# 补充自定义的异常处理
	if response is not None:
		response.data['status_code'] = response.status_code
	return response

在配置文件中声明自定义的异常处理

REST_FRAMEWORK = {
    "EXCEPTION_HANDLER": "demo_app.utils.custom_exception_handler",  # 自定义的异常处理
}

2、补充异常处理

from  rest_framework import status
from django.db import DatabaseError
from rest_framework.response import Response
def custom_exception_handler(exc,context):
	# 先调用 REST framework 默认的异常处理方法获得标准错误响应对象
	response = exception_handler(exc,context)
	# 补充自定义的异常处理
	if response is None:
		view = context['view']
		if isinstance(exc,DatabaseError):
			print('[%s]:%s'%(view,exc))
			response = Response({'detail':'服务器错误'},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
	return response

3、统一异常拦截

REST_FRAMEWORK = {
    "EXCEPTION_HANDLER": "demo_app.exception.CustomExceptionHandler",  # 自定义的异常处理
}

统一响应

# json_response.py
# -*- coding: utf-8 -*-
from rest_framework.response import Response
class SuccessResponse(Response):
    """
    标准响应成功的返回, SuccessResponse(data)或者SuccessResponse(data=data)
    (1)默认code返回2000, 不支持指定其他返回码
    """
    def __init__(self, data=None, msg='success', status=None, template_name=None, headers=None, exception=False,
                 content_type=None,page=1,limit=1,total=1):
        std_data = {
            "code": 200,
            "data": {
                "page": page,
                "limit": limit,
                "total": total,
                "data": data
            },
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class DetailResponse(Response):
    """
    不包含分页信息的接口返回,主要用于单条数据查询
    """
    def __init__(self, data=None,
                 msg='success',
                 status=None,
                 template_name=None,
                 headers=None,
                 exception=False,
                 content_type=None,):
        std_data = {
            "code": 200,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class ErrorResponse(Response):
    """
    标准响应错误的返回,ErrorResponse(msg='xxx')
    """
    def __init__(self, data=None, msg='error', code=500, status=None, template_name=None, headers=None,
                 exception=False, content_type=None):
        std_data = {
            "code": code,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)

自定义异常处理

# -*- coding: utf-8 -*-
# exception
"""自定义异常处理"""
import logging
import traceback

from django.db.models import ProtectedError, RestrictedError
from django.http import Http404
from rest_framework.exceptions import APIException as DRFAPIException, AuthenticationFailed, PermissionDenied
from rest_framework.status import HTTP_407_PROXY_AUTHENTICATION_REQUIRED, HTTP_401_UNAUTHORIZED
from rest_framework.views import set_rollback, exception_handler
from .json_response import ErrorResponse
logger = logging.getLogger(__name__)
def CustomExceptionHandler(ex, context):
    """
    统一异常拦截处理
    目的:(1)取消所有的500异常响应,统一响应为标准错误返回
        (2)准确显示错误信息
    :param ex:
    :param context:
    :return:
    """
    msg = ""
    code = 999
    # 调用默认的异常处理函数
    response = exception_handler(ex, context)
    if isinstance(ex, AuthenticationFailed):
        # 如果是身份验证错误
        if response and response.data.get("detail") == "Given token not valid for any token type":
            code = 401
            msg = ex.detail
        elif response and response.data.get("detail") == "Token is blacklisted":
            # token在黑名单
            return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
        else:
            code = 401
            msg = ex.detail
    elif isinstance(ex, Http404):
        code = 400
        msg = "接口地址不正确"
    elif isinstance(ex, DRFAPIException):
        set_rollback()
        msg = ex.detail
        if isinstance(ex, PermissionDenied):
            msg = f'{msg} ({context["request"].method}: {context["request"].path})'
        if isinstance(msg, dict):
            for k, v in msg.items():
                for i in v:
                    msg = "%s:%s" % (k, i)
    elif isinstance(ex, (ProtectedError, RestrictedError)):
        set_rollback()
        msg = "无法删除:该条数据与其他数据有相关绑定"
    # elif isinstance(ex, DatabaseError):
    #     set_rollback()
    #     msg = "接口服务器异常,请联系管理员"
    elif isinstance(ex, Exception):
        logger.exception(traceback.format_exc())
        msg = str(ex)
    return ErrorResponse(msg=msg, code=code)

二、rest_framework 认证

1、全局

可以在配置文件中配置全局默认的认证方案

# settings.py
REST_FRAMEWORK = {
    "DATETIME_FORMAT": "%Y-%m-%d %H:%M:%S",  # 日期时间格式配置
    "DATE_FORMAT": "%Y-%m-%d",
    "DEFAULT_AUTHENTICATION_CLASSES": (
        # "rest_framework_simplejwt.authentication.JWTAuthentication",# JWT 认证 # pip install djangorestframework-simplejwt
        "rest_framework.authentication.BasicAuthentication", # 基础认证
        "rest_framework.authentication.SessionAuthentication", # session 认证
    )
}

2、局部

也可以在视图中通过设置 authentication_classess 属性来设置 局部认证

from django.db.models import Q
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
from rest_framework.authentication import SessionAuthentication,BasicAuthentication
from .serializers import BookSerializer
from .models import Book
from rest_framework.response import Response
class BookModelViewSet(ModelViewSet):
	authentication_classes = (SessionAuthentication,BasicAuthentication)
    ...

认证失败会有两种可能的返回值

  • 401 Unauthorized 未认证
  • 403 Permission Denied 权限被禁止

三、JWT 认证

1、安装包

pip install djangorestframework-simplejwt

2、settings.py配置

# settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework_simplejwt',
    # 下面这个app用于刷新refresh_token后,将旧的加到到blacklist时使用
    'rest_framework_simplejwt.token_blacklist'
    # ...
]
# ================================================= #
# ***************** REST_FRAMEWORK配置 ************ #
# ================================================= #
REST_FRAMEWORK = {
    ...
    "DEFAULT_AUTHENTICATION_CLASSES": (
        ...
        "rest_framework_simplejwt.authentication.JWTAuthentication",# JWT 认证 # pip install djangorestframework-simplejwt
    )
}

# ================================================= #
# ****************** simplejwt配置 ***************** #
# ================================================= #
# 选择自己需要配置即可
from datetime import timedelta
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),  # Access Token的有效期
    'REFRESH_TOKEN_LIFETIME': timedelta(days=7),  # Refresh Token的有效期

    # 对于大部分情况,设置以上两项就可以了,以下为默认配置项目,可根据需要进行调整

    # 是否自动刷新Refresh Token
    'ROTATE_REFRESH_TOKENS': False,
    # 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌。需要将'rest_framework_simplejwt.token_blacklist'加入到'INSTALLED_APPS'的配置中
    'BLACKLIST_AFTER_ROTATION': False,
    'ALGORITHM': 'HS256',  # 加密算法
    'SIGNING_KEY': SECRET_KEY,  # 签名密匙,这里使用Django的SECRET_KEY

    # 如为True,则在每次使用访问令牌进行身份验证时,更新用户最后登录时间
    "UPDATE_LAST_LOGIN": False,
    # 用于验证JWT签名的密钥返回的内容。可以是字符串形式的密钥,也可以是一个字典。
    "VERIFYING_KEY": "",
    "AUDIENCE": None,  # JWT中的"Audience"声明,用于指定该JWT的预期接收者。
    "ISSUER": None,  # JWT中的"Issuer"声明,用于指定该JWT的发行者。
    "JSON_ENCODER": None,  # 用于序列化JWT负载的JSON编码器。默认为Django的JSON编码器。
    "JWK_URL": None,  # 包含公钥的URL,用于验证JWT签名。
    "LEEWAY": 0,  # 允许的时钟偏差量,以秒为单位。用于在验证JWT的过期时间和生效时间时考虑时钟偏差。

    # 用于指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer"
    "AUTH_HEADER_TYPES": ("Bearer",),
    # 包含JWT的HTTP请求头的名称。默认为"HTTP_AUTHORIZATION"
    "AUTH_HEADER_NAME": "HTTP_AUTHORIZATION",
    # 用户模型中用作用户ID的字段。默认为"id"。
    "USER_ID_FIELD": "id",
    # JWT负载中包含用户ID的声明。默认为"user_id"。
    "USER_ID_CLAIM": "user_id",

    # 用于指定用户身份验证规则的函数或方法。默认使用Django的默认身份验证方法进行身份验证。
    "USER_AUTHENTICATION_RULE": "rest_framework_simplejwt.authentication.default_user_authentication_rule",

    #  用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken"。
    "AUTH_TOKEN_CLASSES": ("rest_framework_simplejwt.tokens.AccessToken",),
    # JWT负载中包含令牌类型的声明。默认为"token_type"。
    "TOKEN_TYPE_CLAIM": "token_type",
    # 用于指定可以使用的用户模型类。默认为"rest_framework_simplejwt.models.TokenUser"。
    "TOKEN_USER_CLASS": "rest_framework_simplejwt.models.TokenUser",

    # JWT负载中包含JWT ID的声明。默认为"jti"。
    "JTI_CLAIM": "jti",

    # 在使用滑动令牌时,JWT负载中包含刷新令牌过期时间的声明。默认为"refresh_exp"。
    "SLIDING_TOKEN_REFRESH_EXP_CLAIM": "refresh_exp",
    # 滑动令牌的生命周期。默认为5分钟。
    "SLIDING_TOKEN_LIFETIME": timedelta(minutes=5),
    # 滑动令牌可以用于刷新的时间段。默认为1天。
    "SLIDING_TOKEN_REFRESH_LIFETIME": timedelta(days=1),
    # 用于生成访问令牌和刷新令牌的序列化器。
    "TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainPairSerializer",
    # 用于刷新访问令牌的序列化器。默认
    "TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSerializer",
    # 用于验证令牌的序列化器。
    "TOKEN_VERIFY_SERIALIZER": "rest_framework_simplejwt.serializers.TokenVerifySerializer",
    # 用于列出或撤销已失效JWT的序列化器。
    "TOKEN_BLACKLIST_SERIALIZER": "rest_framework_simplejwt.serializers.TokenBlacklistSerializer",
    # 用于生成滑动令牌的序列化器。
    "SLIDING_TOKEN_OBTAIN_SERIALIZER": "rest_framework_simplejwt.serializers.TokenObtainSlidingSerializer",
    # 用于刷新滑动令牌的序列化器。
    "SLIDING_TOKEN_REFRESH_SERIALIZER": "rest_framework_simplejwt.serializers.TokenRefreshSlidingSerializer",
}

# ================================================= #
# ****************** simplejwt配置 ***************** #
# ================================================= #
# 我选择的
from datetime import timedelta
SIMPLE_JWT = {
    # token有效时长
    "ACCESS_TOKEN_LIFETIME": timedelta(minutes=60),
    # token刷新后的有效时间
    "REFRESH_TOKEN_LIFETIME": timedelta(days=1),
    # 设置前缀
    "AUTH_HEADER_TYPES": ("JWT",),# 
    # 是否自动刷新Refresh Token
    "ROTATE_REFRESH_TOKENS": True,
    # 刷新Refresh Token时是否将旧Token加入黑名单,如果设置为False,则旧的刷新令牌仍然可以用于获取新的访问令牌。需要将'rest_framework_simplejwt.token_blacklist'加入到'INSTALLED_APPS'的配置中
    'BLACKLIST_AFTER_ROTATION': True,
    #  用于指定可以使用的令牌类。默认为"rest_framework_simplejwt.tokens.AccessToken"
    'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
}

执行数据库迁移:

python manage.py  makemigrations
python manage.py  migrate

注意1 AUTH_HEADER_TYPES:

当配置了 AUTH_HEADER_TYPES 指定JWT在HTTP请求头中使用的身份验证方案。默认为"Bearer";在使用 postman和其他的一些自带的认证中会在请求同中增加Bearer + token 字符串。在自定义Header 中可以自己进行增加内容。如我使用的是JWT开头的那么在 postman中

在这里插入图片描述

源码查看

每次访问该视图时,都会调用**JSONWebTokenAuthentication.authenticate** 进行认证.

from rest_framework_simplejwt.authentication import JWTAuthentication
user, tokrn = JWTAuthentication().authenticate(request)

在源码 authenticate 方法中获取

在这里插入图片描述

当不匹配时携带请求头会出现: 身份认证信息未提供。

在这里插入图片描述

在这里插入图片描述

3、提供获取和刷新token的url

from django.urls import path,include,re_path
from rest_framework.documentation import include_docs_urls

from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
    TokenVerifyView
)
urlpatterns = [
    # jwt 认证
    path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
    # 下面这个是用来验证 token 的,根据需要进行配置
    path('token/verify/', TokenVerifyView.as_view(), name='token_verify'),
]

在这里插入图片描述

在这里插入图片描述

4、自定义返回信息

该序列化器需继承TokenObtainPairSerializer类,可以在任意一个app中的seralizers.py中增加该自定义的序列化器,并重写了get_token()方法。在这个方法中,我们可以自定义Payload,将用户的信息添加到Token中。

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class LoginSerializer(TokenObtainPairSerializer):
    def get_token(cls, user):
        token = super().get_token(user)
        # 增加想要加到token中的信息
        token['username'] = user.username
        token['email'] = user.email
        return token
# 改写simple JWT提供的默认视图,在app01/views中新增一个视图,该视图需继承至默认的视图类TokenObtainPairView
class LoginView(TokenObtainPairView):
    """
    登录接口
    """
    serializer_class = LoginSerializer
    permission_classes = []
#urls.py
from demo_app.login import LoginView

urlpatterns = [
    # 登录
    path('login/',LoginView.as_view(),name='login'),
]

在这里插入图片描述

在这里插入图片描述

5、简单实现登录

5.1 账号或密码错误

TokenObtainSerializer 中登录登录失败提示:

在这里插入图片描述

重写登陆提示:

from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from django.contrib.auth.models import User # 使用的是默认的django 用户
class LoginSerializer(TokenObtainPairSerializer):
    class Meta:
        model = User
        fields = "__all__"
        read_only_fields = ["id","username","email"]
    def get_token(cls,user):
        ''' 重写 获取 token中保存的内容 '''
        token = super().get_token(user)
        # 增加想要加到token中的信息
        token['username'] = user.username
        token['email'] = user.email
        return token
    # 登录失败提示
    default_error_messages = {"no_active_account": '账号/密码错误'}
    # 重写登录方法
    def validate(self,attrs):
      ''' 重写登录方法 '''
      # 获取传入的 用户名和密码
      username = self.initial_data.get("username", None)
      password = self.initial_data.get("password", None)
      print("username:",username,"\npassword:",password)
      data = super().validate(attrs)#获取 返回的 token内容
      return {"code": 200, "msg": "请求成功", "data": data}

class LoginView(TokenObtainPairView):
    """
    登录接口
    """
    serializer_class = LoginSerializer
    permission_classes = []

在这里插入图片描述

5.2 增加统一返回

# -*- coding: utf-8 -*-
# 统一返回格式
from rest_framework.response import Response
class SuccessResponse(Response):
    """
    标准响应成功的返回, SuccessResponse(data)或者SuccessResponse(data=data)
    (1)默认code返回2000, 不支持指定其他返回码
    """

    def __init__(self, data=None, msg='success', status=None, template_name=None, headers=None, exception=False,
                 content_type=None,page=1,limit=1,total=1):
        std_data = {
            "code": 200,
            "data": {
                "page": page,
                "limit": limit,
                "total": total,
                "data": data
            },
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class DetailResponse(Response):
    """
    不包含分页信息的接口返回,主要用于单条数据查询
    (1)默认code返回 200, 不支持指定其他返回码
    """

    def __init__(self, data=None,
                 msg='success',
                 status=None,
                 template_name=None,
                 headers=None,
                 exception=False,
                 content_type=None,):
        std_data = {
            "code": 200,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class ErrorResponse(Response):
    """
    标准响应错误的返回,ErrorResponse(msg='xxx')
    (1)默认错误码返回 500, 也可以指定其他返回码:ErrorResponse(code=xxx)
    """

    def __init__(self, data=None, msg='error', code=500, status=None, template_name=None, headers=None,
                 exception=False, content_type=None):
        std_data = {
            "code": code,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)
# -*- coding: utf-8 -*-
from rest_framework.response import Response

class SuccessResponse(Response):
    """
    标准响应成功的返回, SuccessResponse(data)或者SuccessResponse(data=data)
    (1)默认code返回2000, 不支持指定其他返回码
    """

    def __init__(self, data=None, msg='success', status=None, template_name=None, headers=None, exception=False,
                 content_type=None,page=1,limit=1,total=1):
        std_data = {
            "code": 200,
            "data": {
                "page": page,
                "limit": limit,
                "total": total,
                "data": data
            },
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class DetailResponse(Response):
    """
    不包含分页信息的接口返回,主要用于单条数据查询
    (1)默认code返回 200, 不支持指定其他返回码
    """

    def __init__(self, data=None,
                 msg='success',
                 status=None,
                 template_name=None,
                 headers=None,
                 exception=False,
                 content_type=None,):
        std_data = {
            "code": 200,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)


class ErrorResponse(Response):
    """
    标准响应错误的返回,ErrorResponse(msg='xxx')
    (1)默认错误码返回 500, 也可以指定其他返回码:ErrorResponse(code=xxx)
    """

    def __init__(self, data=None, msg='error', code=500, status=None, template_name=None, headers=None,
                 exception=False, content_type=None):
        std_data = {
            "code": code,
            "data": data,
            "msg": msg
        }
        super().__init__(std_data, status, template_name, headers, exception, content_type)

异常处理

# -*- coding: utf-8 -*-
"""自定义异常处理"""
import logging
import traceback

from django.db.models import ProtectedError, RestrictedError
from django.http import Http404
from rest_framework.exceptions import APIException as DRFAPIException, AuthenticationFailed, PermissionDenied
from rest_framework.status import HTTP_407_PROXY_AUTHENTICATION_REQUIRED, HTTP_401_UNAUTHORIZED
from rest_framework.views import set_rollback, exception_handler
from .json_response import ErrorResponse
logger = logging.getLogger(__name__)
def CustomExceptionHandler(ex, context):
    """
    统一异常拦截处理
    目的:(1)取消所有的500异常响应,统一响应为标准错误返回
        (2)准确显示错误信息
    :param ex:
    :param context:
    :return:
    """
    msg = ""
    code = 500
    # 调用默认的异常处理函数
    response = exception_handler(ex, context)
    if isinstance(ex, AuthenticationFailed):
        # 如果是身份验证错误
        if response and response.data.get("detail") == "Given token not valid for any token type":
            code = 401
            msg = ex.detail
        elif response and response.data.get("detail") == "Token is blacklisted":
            # token在黑名单
            return ErrorResponse(status=HTTP_401_UNAUTHORIZED)
        else:
            code = 401
            msg = ex.detail
    elif isinstance(ex, Http404):
        code = 400
        msg = "接口地址不正确"
    elif isinstance(ex, DRFAPIException):
        set_rollback()
        msg = ex.detail
        if isinstance(ex, PermissionDenied):
            msg = f'{msg} ({context["request"].method}: {context["request"].path})'
        if isinstance(msg, dict):
            for k, v in msg.items():
                for i in v:
                    msg = "%s:%s" % (k, i)
    elif isinstance(ex, (ProtectedError, RestrictedError)):
        set_rollback()
        msg = "无法删除:该条数据与其他数据有相关绑定"
    # elif isinstance(ex, DatabaseError):
    #     set_rollback()
    #     msg = "接口服务器异常,请联系管理员"
    elif isinstance(ex, Exception):
        logger.exception(traceback.format_exc())
        msg = str(ex)
    return ErrorResponse(msg=msg, code=code)

登录

# coding=utf-8
'''
@date:2023/11/29 11:15
@mail:xiaochun235@qq.com
@Content:
'''
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken, AccessToken
from rest_framework.exceptions import APIException
from rest_framework.views import APIView
from django.contrib.auth.models import User
from .json_response import DetailResponse
class LoginSerializer(TokenObtainPairSerializer):
    class Meta:
        model = User
        fields = "__all__"
        read_only_fields = ["id","username","email"]
    def get_token(cls,user):
        ''' 重写 获取 token中保存的内容 '''
        token = super().get_token(user)
        # 增加想要加到token中的信息
        token['username'] = user.username
        token['email'] = user.email
        return token

    default_error_messages = {"no_active_account": '账号/密码错误'}
    # 重写登录方法
    def validate(self,attrs):
      ''' 重写登录方法 '''
      #
      username = self.initial_data.get("username", None)
      password = self.initial_data.get("password", None)
      print("username:",username,"\npassword:",password)
      # 可以 增加图片验证码验证,短信验证码验证等登录验证因子
      # if True:
      #     raise CustomValidationError("验证码不正确")
      # 获取 返回的 token内容
      data = super().validate(attrs)
      return {"code": 2000, "msg": "请求成功", "data": data}


# 改写simple JWT提供的默认视图,在app01/views中新增一个视图,该视图需继承至默认的视图类TokenObtainPairView
class LoginView(TokenObtainPairView):
    """
    登录接口
    """
    serializer_class = LoginSerializer
    permission_classes = []

class CustomValidationError(APIException):
    """
    继承并重写验证器返回的结果,避免暴露字段
    """
    def __init__(self, detail):
        self.detail = detail

在这里插入图片描述

6、修改登录配置

settings.py 中修改登录配置

# ================================================= #
# ******************** 登录方式配置 ******************** #
# ================================================= #
AUTHENTICATION_BACKENDS = ["demo_app.backends.CustomBackend"]

重写等

from django.contrib.auth.backends import ModelBackend
from django.contrib.auth.hashers import check_password
from django.db.models import Q
from django.contrib.auth.models import User
from rest_framework.exceptions import APIException
class CustomValidationError(APIException):
    """
    继承并重写验证器返回的结果,避免暴露字段
    """
    def __init__(self, detail):
        self.detail = detail
class CustomBackend(ModelBackend):
    """
    重写认证登录
    """
    def authenticate(self, request, username=None, password=None, **kwargs):
        # 查询用户对象  可通过密码和邮箱登录
        user = User.objects.filter(Q(username=username) | Q(email=username)).first()
        # 判断 用户,密码, 是否是管理员
        if user and check_password(password, user.password) and user.is_staff:
            return user  # 验证通过返回对象 否则返回None
        else:
            raise CustomValidationError("登录用户账号或密码错误!")

在这里插入图片描述

7、刷新token测试

刷新 token 请求必须携带 refresh 参数并发起 POST 请求
在这里插入图片描述重写 refresh 刷新

from rest_framework_simplejwt.views import ,TokenRefreshView
from rest_framework_simplejwt.tokens import RefreshToken
class CustomTokenRefreshView(TokenRefreshView):
    def post(self, request, *args, **kwargs):
        # 调用父类的post方法获取原始的刷新令牌操作结果
        response = super().post(request, *args, **kwargs)
        # 从响应中获取刷新令牌
        refresh_token = RefreshToken(response.data['refresh'])

        # 创建自定义的返回数据
        custom_response_data = {
            'access': str(refresh_token.access_token),
            'refresh': str(refresh_token),
            'userId': refresh_token.get('userId'),
            'name': refresh_token.get('name'),
            'user_type': refresh_token.get('user_type'),
            'role_info': [],
        }

        # 返回自定义的响应数据
        return DetailResponse(msg="刷新token成功", data=custom_response_data)

在这里插入图片描述

其他笔记

序列化器

视图路由

异常、登录认证

权限、限流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值