Django-jwt token生成源码分析

Posted maoruqiang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django-jwt token生成源码分析相关的知识,希望对你有一定的参考价值。

一. 认证的发展历程简介

  这里真的很简单的提一下认证的发展历程。以前大都是采用cookie、session的形式来进行客户端的认证,带来的结果就是在数据库上大量存储session导致数据库压力增大,大致流程如下:

技术图片

  在该场景下,分布式、集群、缓存数据库应运而生,认证的过程大致如下:

技术图片

   不过该方式还是缓解不了数据库压力,一个项目中应该尽可能多的减少IO操作,于是后来采用签名的方式,在服务端只保存token的签名算法,当客户端认证时,只需用算法去生成或是判断token的合法性即可。大致方式如下:
技术图片

二. JWT签发Token源码分析

2.1 JWT工作原理及简介

  Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519)。该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

  JWT由三部分组成,分别是头.载荷.签名(header.payload.signature),格式如下:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6Im93ZW4iLCJleHAiOjE1NTgzMDM1NDR9
.4j5QypLwufjpqoScwUB9LYiuhYcTw1y4dPrvnv7DUyo
  •  header:一般存放如何处理token的方式:加密的算法、是否有签名等
  • payload:数据的主体部分:用户信息、发行者、过期时间等
  • signature:签名:将header、payload再结合密码盐整体处理一下

  其中JWT的头和载荷均采用base64加密,是可以被反解的,主要用于反解之后提取用户信息、过期时间等。而签名部分采用摘要算法SHA256不可逆加密,里面掺杂了密钥,密钥是存储于Django中的固定字符串,主要用于认证token。

jwt = base64(头部).base64(载荷).hash256(base64(头部).base(载荷).密钥)

 

   JWT官网:https://github.com/jpadilla/django-rest-framework-jwt

2.2 JWT生成token源码分析

  首先,django-jwt提供了一个生成token的接口,先安装django-jwt模块:

pip install djangorestframework-jwt

   首先看看jwt的视图views.py:

技术图片

  因为是CBV,通过类名.as_view()调用视图类,而jwt中已经有加了as_view的属性:
技术图片

  所以在路由urls.py中,我们只需导入该属性即可(path是django2.x版本的语法,不是正则匹配):

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


urlpatterns = [
    path(get_token/, obtain_jwt_token)
]

  因为生成token是在用户登录的时候,所以是post请求,依据属性查找顺序找到post方法:
技术图片

  看看post方法的详情:

技术图片

  首先看看get_serilizer方法:

技术图片

  get_serializer_class方法:

技术图片

  生成token的类中的serializer_class:
技术图片

  所以post方法中的get_serializer方法返回的是JSONWebTokenSerializer的对象:

技术图片

  其中username_field是封装的方法,PasswordField一看就是类:

技术图片

技术图片

技术图片

  还有USERNAME_FIELD默认就叫username:

技术图片

 

  由上述可以得知get_serializer方法返回的是一个反序列化后的对象,而且该序列化还提供了全局钩子。

   现在post方法接着往下走,让我们看看其中的jwt_response_payload_handler方法,依据数据的查找顺序:
技术图片

  点击api_settings中,查找JWT_RESPONSE_PAYLOAD_HANDLER

技术图片

技术图片

  所以要让它采用自定义的函数时,只需要在项目中的settings.py中加Jwt-AUTH字典,然后在里面填写相应配置即可。

  那么看看jwt_response_payload_handler
技术图片

   走到这里,发现token已经有了,那么是在哪一步生成的呢?没错,就是当序列化通过时,执行is_valid时会执行序列化类的钩子函数,而token就是在全局钩子函数中生成的:

技术图片

  先看看all(credentials.values())方法:
技术图片

  再往下走authenticate方法,其中的credentials就是用户名与密码:

技术图片

  看看_get_backends(return_tuples=True)方法:

技术图片

  首先点击因为settings是django.conf中的,我们直接点击通过最上方导入的conf点击过去,然后通过属性的查找顺序,settings对象是先使用我们项目中的settings.pu配置,其次是django自带的global_settings.py中的配置,依据属性查找顺序,我们找到global_settings.py中的AUTHENTICATION_BACKENDS:

技术图片

  再看看load_backend方法里的import_string(path)():
技术图片

  所以authenticate(request=None, **credentials)方法中的backend.authenticate(request, **credentials)方法实际上就是ModelBackend.authenticate方法:
技术图片

  回到我们的序列化类JSONWebTokenSerializer中的全局钩子:
技术图片

  先来看看jwt_payload_handler(user)方法

技术图片

  点击跳转至api_settings:

技术图片

  找到这两个方法,先来看一下jwt_payload_handler:

技术图片

技术图片

  接下来看看jwt_encode_handler(payload)方法:

技术图片

  上述的jwt.encode就是生成token的方法接下来看看他里面的几个参数。

  设置中JWT_PRIVATE_KEY值为None,所以会走jwt_get_secret_key

技术图片

  因为默认配置中JWT_GET_USER_SECRET_KEY值也为None,所以直接返回JWT_SECRET_KEY:
技术图片

  最终返回的是django项目setting.py中的SECRET_KEY,是一串无序的字符串,用于JWT的签名。

  走到这里,JWT生成token的源码差不多都走一遍了。

 

以上是关于Django-jwt token生成源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Django中间件CsrfViewMiddleware源码分析

Spring Security Oauth2 Token 提取流程源码分析

thriftpy+ply源码分析

TokenAutication源码分析

ZooKeeper源码分析-Jute-第一部分

区块链入门教程以太坊源码分析p2p-rlpx节点之间的加密链路二