jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

JWT内置签发方法修改返回格式

JWT的内置签发方法只会返回一个token串,但大部分情况,我们是需要返回用户名或者其他的一些信息,这时候我们就需要针对返回格式做出一些修改:

在url.py中配置好内置的登录路由:

from django.contrib import admin
from django.urls import path
from rest_framework_jwt.views import obtain_jwt_token

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', obtain_jwt_token),
]

创建一个py文件写一个函数,utils.py:

'''
这里的函数名字不是固定的可以随意取
但是传的参数是固定的
后面return什么,前端就会显示什么
'''

def jwt_response_payload_handler(token, user, request):
    return {
        'token': token,
        'username': user.username
    }

在settings.py文件里将这个函数配置进JWT_AUTH里面:

# drf_jwt的认证配置(drf_jwt有个默认配置文件)

JWT_AUTH = {
    'JWT_RESPONSE_PAYLOAD_HANDLER': 'app01.utils.jwt_response_payload_handler'
}

这样配置完成之后就可以在返回信息中显示用户名了:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

drf_jwt签发的源码分析

由路由的obtain_jwt_token>ObtainJSONWebToken(JSONWebTokenAPIView)>视图类ObtainJSONWebToken(JSONWebTokenAPIView)

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

可以看到我们urls.py中配置的路由obtain_jwt_token其实就是一个视图类:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

我们再来看看这个视图类,发现视图类只有一行代码,一个序列化类,但是我们明明发的是post请求,这个视图里面却没有post方法,那肯定在父类里面:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

进入到父类这里可以看到这个视图类帮我们做了处理,因为这本来就是一个登录的视图类,就不该有权限和认证,所有放了两个空元组:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

JSONWebTokenAPIView内有个post方法,重点开始了,它将request.data进行序列化,得到序列化对象,然后对这个序列化对象进行了校验,会执行字段自己的校验规则,局部钩子,全局钩子。

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

其实这里的序列化类就是一开始我们看到的序列化类JSONWebTokenSerializer:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

那我们来看一下这个JSONWebTokenSerializer序列化类:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

看完__ init __之后来到了我们做重要的validate:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

执行完validate之后,我们又需要返回到.isvalid看看之后是怎么处理的:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

drf_jwt认证token源码

读JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

进入到authenticate方法,这一句其实就是取出了Authorization的value值:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

那么它是怎么去取的呢,我们需要回到它的子类JSONWebTokenAuthentication,看get_jwt_value方法:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

所以从get_authorization_header就可以知道get_jwt_value的意思了:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

这就是authenticate第一句的意思,它将token取了出来,让我们再来看看authenticate:

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

jwt内置签发方法修改返回格式 drf_jwt签发认证源码 自定义用户表签发token

自定义用户表签发token

使用自定义的表签发token,既然是自己写的,那很多东西都可以自己来定义,比如我们可以不使用权限类。

views.py

from rest_framework.views import APIView
from app01.models import UserInfo
from rest_framework_jwt.settings import api_settings
from rest_framework.response import Response

# 从 rest_framework_jwt.settings.api_settings中导入生成token方法
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER


class LoginView(APIView):
    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = UserInfo.objects.filter(username=username, password=password).first()
        if user:  # 如果用户名密码正确签发token

            # 将用户信息转码成payload
            payload = jwt_payload_handler(user)

            # 将payload加上header和signature生成token
            token = jwt_encode_handler(payload)
            return Response({
                'code': '200',
                'msg': '登录成功',
                'token': token,
                'username': username
            })
        else:
            return Response({'code': '101', 'msg': '用户名或密码错误'})

urls.py中配置

from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.LoginView.as_view())
]

自定义用户表的认证类

创建auth.py定义自己的认证类

# 使用自己的认证类来认证token
import jwt
from rest_framework import exceptions
from rest_framework_jwt.authentication import BaseAuthentication
from rest_framework_jwt.settings import api_settings
from app01.models import UserInfo

jwt_decode_handler = api_settings.JWT_DECODE_HANDLER


class JwtAuthentication(BaseAuthentication):
    def authenticate(self, request):
        # 取出前端传入的token
        token = request.META.get('HTTP_TOKEN')
        # 通过token获取payload
        try:
            payload = jwt_decode_handler(token)
        except jwt.ExpiredSignatureError:
            msg = '签名过期'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.DecodeError:
            msg = '解码错误'
            raise exceptions.AuthenticationFailed(msg)
        except jwt.InvalidTokenError:
            raise exceptions.AuthenticationFailed()

        # 查库获取到user对象
        # user = UserInfo.objects.filter(pk=payload['user_id']).first()

        """
        可以针对查库做优化,如果不是必要可以直接从token的payload中去取,不用每次都查库
        但是也有弊端,就是只能获取到user_id和username
        """

        # 优化方式一:
        # user = UserInfo(id=payload['user_id'], username=payload['username'])
        # 优化方式二:
        user = {'id': payload['user_id'], 'username': payload['username']}

        # 返回当前用户
        return user, token

将该认证类配置在视图类BookView当中:

from app01.models import Book
from app01.serializer import BookSerializer
from rest_framework.generics import GenericAPIView
from rest_framework.mixins import ListModelMixin
from app01.auth import JwtAuthentication


class BookView(ListModelMixin, GenericAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    authentication_classes = [JwtAuthentication]

    def get(self, request):
        return self.list(request)

这样便完成了自定义类。

上一篇:记录SpringSecurity jwt问题


下一篇:day72