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'
}
这样配置完成之后就可以在返回信息中显示用户名了:
drf_jwt签发的源码分析
由路由的obtain_jwt_token>ObtainJSONWebToken(JSONWebTokenAPIView)>视图类ObtainJSONWebToken(JSONWebTokenAPIView)
可以看到我们urls.py中配置的路由obtain_jwt_token其实就是一个视图类:
我们再来看看这个视图类,发现视图类只有一行代码,一个序列化类,但是我们明明发的是post请求,这个视图里面却没有post方法,那肯定在父类里面:
进入到父类这里可以看到这个视图类帮我们做了处理,因为这本来就是一个登录的视图类,就不该有权限和认证,所有放了两个空元组:
JSONWebTokenAPIView内有个post方法,重点开始了,它将request.data进行序列化,得到序列化对象,然后对这个序列化对象进行了校验,会执行字段自己的校验规则,局部钩子,全局钩子。
其实这里的序列化类就是一开始我们看到的序列化类JSONWebTokenSerializer:
那我们来看一下这个JSONWebTokenSerializer序列化类:
看完__ init __之后来到了我们做重要的validate:
执行完validate之后,我们又需要返回到.isvalid看看之后是怎么处理的:
drf_jwt认证token源码
读JSONWebTokenAuthentication的父类BaseJSONWebTokenAuthentication的authenticate
进入到authenticate方法,这一句其实就是取出了Authorization的value值:
那么它是怎么去取的呢,我们需要回到它的子类JSONWebTokenAuthentication,看get_jwt_value方法:
所以从get_authorization_header就可以知道get_jwt_value的意思了:
这就是authenticate第一句的意思,它将token取了出来,让我们再来看看authenticate:
自定义用户表签发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)
这样便完成了自定义类。