一. 认证的发展历程简介

  这里真的很简单的提一下认证的发展历程。以前大都是采用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

  可以重写该方法增加返回给前端的值,同时需要在配置文件中配置JWT_RESPONSE_PAYLOAD_HANDLER:

from .serializers import UserModelSerializers
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': UserModelSerializer(user).data
}
# restful 规范
# return {
# 'status': 0,
# 'msg': 'OK',
# 'data': {
# 'token': token,
# 'username': user.username
# }
# }

  配置文件中添加以下配置:

JWT_AUTH = {
# 自定义认证结果:见下方序列化user和自定义response
'JWT_RESPONSE_PAYLOAD_HANDLER': 'user.utils.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方法:

  这里意味着我们可以重写ModelBackend的authenticate实现多方式登录,因为只需要最终返回一个user对象或者None就行:

from django.contrib.auth.backends import ModelBackend
from .models import User
import re
class JWTModelBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
"""
:param request:
:param username: 前台传入的用户名
:param password: 前台传入的密码
:param kwargs:
:return:
"""
try:
if re.match(r'^1[3-9]\d{9}$', username):
user = User.objects.get(mobile=username)
elif re.match(r'.*@.*', username):
user = User.objects.get(email=username)
else:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None # 认证失败就返回None即可,jwt就无法删除token
# 用户存在,密码校验通过,是活着的用户 is_active字段为1
if user and user.check_password(password) and self.user_can_authenticate(user):
return user # 认证通过返回用户,交给jwt生成token

  回到我们的序列化类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的源码差不多都走一遍了。然后我们可以在views.py中导入jwt_payload_handler和jwt_encode_handler实现手动签发token。

from rest_framework_jwt.settings import api_settings

jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload) # 了解,原生视图
# 原生APIView可以实现手动签发 jwt
class LoginAPIView(APIView):
def post(self):
# 完成手动签发
pass

JWT签发token的更多相关文章

  1. 在node中使用jwt签发与验证token

    1.什么是token token的意思是“令牌”,是服务端生成的一串字符串,作为客户端进行请求的一个标识. token是在服务端产生的.如果前端使用用户名和密码向服务端发送请求认证,服务端认证成功,那 ...

  2. drf中的jwt使用与手动签发token、校验用户

    jwt认证 1)session存储token,需要数据库参与,耗服务器资源.低效2)缓存存token,需要缓存参与,高效,不易集群3)客户端存token,服务器存签发与交易token的算法,高效,易集 ...

  3. 国服最强JWT生成Token做登录校验讲解,看完保证你学会!

    转载于:https://blog.csdn.net/u011277123/article/details/78918390 Free码农 2017-12-28 00:08:02 JWT简介 JWT(j ...

  4. SpringBoot集成JWT实现token验证

    原文:https://www.jianshu.com/p/e88d3f8151db JWT官网: https://jwt.io/ JWT(Java版)的github地址:https://github. ...

  5. 基于jwt的token验证

    一.什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519). 该token被设计为紧凑且安全的,特别适用于分布 ...

  6. 基于JWT的Token登录认证

    1.JWT简介   JSON Web Token(缩写 JWT),是目前最流行的跨域认证解决方案. 2.JWT的原理        JWT的原理是,服务器认证以后,生成一个JSON格式的对象,发回给客 ...

  7. day76_10_23自定义签发token,其他drf组件

    一.签发token的原理 当认证类authentication_classes是JSONWebTokenAuthentication时,其父类JSONWebTokenAPIView只有post 方法, ...

  8. jwt、token

    什么是JWT jwt是一段密文;然而密码是如何产生的? 密码是由三个部分生成: 1.JWT头:JWT头部分是一个描述JWT元数据的JSON对象:{"alg":"hash2 ...

  9. tp5使用jwt生成token,做api的用户认证

    首先 composer 安装  firebase/php-jwt github:https://github.com/firebase/php-jwt composer require firebas ...

随机推荐

  1. 如何正确的hook方法objc_msgSend · jmpews

    如何正确的hook方法objc_msgSend 前言 如果希望对 Objective-C 的方法调用进行 log, 一个很好的解决方法就是 hook 方法 objc_msgSend, 当然想到的就是利 ...

  2. 一个异步访问redis的内存问题

    | 分类 redis  | 遇到一个redis实例突然内存飙高的案例, 具体症状如下: 客户端使用异步访问模式 单个请求的回包很大,hgetall一个8M的key 由于访问量比较大,已经登录不上red ...

  3. Haproxy的应用

    如上图所示,在 192.168.1.0/24 这个网段的客户端想要访问在 172.20.0.0/20 网段内的服务器,所有的通信又不想暴露在互联网上,因此可以在这两个网段内分别都放一台 Haproxy ...

  4. 虚拟桌面软件CitrixReceiver相关问题

    由于安过一次,卸载再次安装程序报错: 原因:卸载不干净,导致再次安装部分插件未成功安装 解决: 1.在控制面板卸载Citrix receiver 2.删除C:\Program Files (x86)\ ...

  5. 用java实现的微信公众号爬虫

    Published: 2016-11-23 In Spider. tags: Spider 版权声明:本文为博主原创文章,未经博主允许不得转载. 思路: 直接从chuansong.me爬取,由于微信公 ...

  6. Animate.css动画库,简单的使用,以及源码剖析

    animate.css是什么?能做些什么? animate.css是一个css动画库,使用它可以很方便的快捷的实现,我们想要的动画效果,而省去了操作js的麻烦.同时呢,它也是一个开源的库,在GitHu ...

  7. 牛客网剑指offer第21题——判断出栈序列是否是入栈序列

    题目: 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序.假设压入栈的所有数字均不相等.例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈 ...

  8. PKI详解

    预备: 一.密码基元 二.密钥管理 三.PKI本质是把非对称密钥管理标准化 PKI 是 Public Key Infrastructure 的缩写,其主要功能是绑定证书持有者的身份和相关的密钥对(通过 ...

  9. Harbor镜像漏洞扫描

    Harbor镜像漏洞扫描 闲聊:我们知道 镜像安全也是容器化建设中一个很重要的环节,像一些商业软件如:Aqua就很专业但是收费也是很昂贵的,今天我们介绍下Harbor自带的镜像扫描器. 一.安装最新版 ...

  10. k8s环境部署.net core web项目(docker本地仓库)

    在之前的文档中,我们部署了.net core web在k8s环境下,达成了集群管理项目的目的.但是,由于是本地部署,需要在所有的node节点都拉取好镜像,这是非常麻烦的,为了只维护一份代码,同步更新. ...