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