JWT

目录

JWT组成

第一部分header

第二部分payload

第三部分signature

注意

JWT认证算法:签发和校验

drf使用jwt

drf项目的jwt认证开发流程

drf-jwt安装和简单使用

安装

简单使用

drf-jwt使用

jwt内置类JSONWebTokenAuthentication

控制使用jwt的登录接口返回的数据

自定制jwt认证类

使用jwt自动签发token+多方式登录

配置jwt token的过期时间


JWT组成

jwt(java web token)本质就是一个token,是一个字符串,由三段信息组成:

        header(头部) + payload(荷载) + signature(签证)

比如:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

jwt定义来源优势种类: JWT详解_baobao555#的博客-CSDN博客

第一部分header

header承载两部分信息:类型(声明是jwt)和加密算法(声明加密算法,一般都是使用HMAC SHA256)
完整头部信息就比如下面的json数据:{''type'':''JWT'',"alg":"HS256"}
然后将头部进行base64加密(该加密是可以对称解密的),就构成了第一部分header:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

第二部分payload

存放有效信息的地方,有效信息包含三部分:1 标准中注册的声明iss: jwt签发者sub: jwt所面向的用户aud: 接收jwt的一方exp: jwt的过期时间,这个过期时间必须要大于签发时间nbf: 定义在什么时间之前,该jwt都是不可用的.iat: jwt的签发时间jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避时序攻击。2 公共的声明公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密。3 私有的声明私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
比如定义一个payload{"sub": "123456789","name": "weer","admin": true}
然后将其进行base64加密,得到JWT的第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

第三部分signature

signature是个签证信息,由三部分构成header(base64后的)+payload(base64后的)+secret
signature将经base64加密后的header和payload连接组成字符串,
然后通过header中声明的加密算法进行加盐secret组合加密,然后就形成了signature。

将header、payload、signature连接成一个字符串就形成了最终的jwt。

注意

secret是保存在服务端的,jwt的签发认证也是在服务端进行。secret就是用来进行jwt的签发和jwt的验证,所以secret就是你服务端的私钥,在任何时候都不得流露出去一旦客户端搞到了它意味着客户端就可以自己签发jwt了,那服务端的认证就没用了严重威胁数据安全。

JWT认证算法:签发和校验

jwt简言分为三段式:头、体、签名(header,payload,signature)。头和体是可逆加密,服务器可以反解出user对象;签名是不可逆加密,保证整个token安全性。

jwt组成的三部分都是采用json格式字符串进行加密,可逆加密一般用base64算法,不可逆加密一般采用hash(/md5)算法。

头中内容通常为基本信息:公司信息,项目组信息,token采用的加密方式信息等

体中内容是关键信息:用户主键、用户名、签发时客户端信息(ip,地址),过期时间等

签名内容为安全信息:头的加密结果,体的加密结果,服务器保密的安全码/私钥,不可逆加密

签发就是传数据信息给jwt,jwt自动给你返回一个token(jwt token);

# 签发:根据登录请求提交来的账号+密码+设备信息签发token
"""
(1) 用基本信息存储json字典,采用base64算法加密得到  头字符串
(2) 用关键信息存储json字典,采用base64算法加密得到  体字符串
(3) 用头、体加密字符串再加安全码信息存储json字典,采用hash md5算法加密得到  签名字符串
"""

校验就是jwt提供的认证方法,可以用它进行用户校验、权限校验等。

# 校验
"""
(1) 将token拆分成三段字符串,第一段为头加密字符串,一般不需做任何处理
(2) 第二段 体加密字符串,要反解主键,通过表就能得到登录用户,过期时间和设备信息都是安全信息,确保token没过期,且时间同一设备过来的
(3) 再用第一段+第二段+服务器安全码 进行不可逆md5加密,与第三段签名字符串进行碰撞校验,通过后才能代表第二段校验得到的对象,那就是合法登录用户
"""

drf使用jwt

关于签发和校验,我们可以使用drf中与jwt有关的第三方包来实现

drf项目的jwt认证开发流程

"""

1、用账号密码访问登录接口,登录接口逻辑中调用,通过签发token的相关算法得到token,返回给客户端,客户端存到自己的cookie中

2、校验token的算法应该写在认证类中(在认证类中调用),全局配置给认证组件,所有视图类请求都会进行认证校验,所以请求里带了token就会反解出user用户对象,在视图类中使用request.user就能访问登录的用户

注:登录接口需要做 认证+权限 两个局部禁用
"""

drf-jwt安装和简单使用

安装

pip3 install djangorestframework-jwt

简单使用

创建超级用户weer

在url.py中配置了一个登录路由

from rest_framework_jwt.views import ObtainJSONWebToken,VerifyJSONWebToken,RefreshJSONWebToken

"""

drf-jwt内置了三种jwt视图类,三种视图类都继承了基类JSONWebTokenAPIView---看源码

基类JSONWebTokenAPIView继承了APIView

"""

from rest_framework_jwt.views import obtain_jwt_token"""源码中自动将三种jwt视图类调用as_view()方法生成对象,所以两种方式等价obtain_jwt_token = ObtainJSONWebToken.as_view()refresh_jwt_token = RefreshJSONWebToken.as_view()verify_jwt_token = VerifyJSONWebToken.as_view()"""
urlpatterns = [# jwt简单使用# path('login/', ObtainJSONWebToken.as_view()), # 与下等价path('login/', obtain_jwt_token),
]

在postman中发post请求,带上用户名密码数据,即可得drf-jwt自动生成的jwt token:

{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJlbWFpbCI6IiIsInVzZXJuYW1lIjoid2VlciIsImVcCI6MTY2NDQ1MzU5NX0.L1Y9IAqc40X-AGcPvbvI1DDo-BD6iqYmg2BQN5zWYSU"}

drf-jwt使用

jwt内置类JSONWebTokenAuthentication

views.py

# jwt内置认证类使用——限制只有登录过的用户才能访问
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework.permissions import IsAuthenticated
class OnlyLoginUserCanSeeAPIView(APIView):# 限制登录用户才能访问需要配以下两个内置类authentication_classes = [JSONWebTokenAuthentication,]# 权限permission_classes = [IsAuthenticated,]def get(self, request):return Response('login user')
class VisitorCanSeeAPIView(APIView):# 游客/用户不登录也能访问只需将permission_classes去掉即可authentication_classes = [JSONWebTokenAuthentication]def get(self, request):return Response('anonymous user')

urls.py

	path('loginusersee/', views.OnlyLoginUserCanSeeAPIView.as_view()),path('visitorusersee/', views.VisitorCanSeeAPIView.as_view()),

此时使用postman get请求访问接口/loginusersee/必须携带登录后生成的jwt token(keyAuthorizationvalueJWT空格+token)才能访问到该视图函数下的信息("login user")

而访问/visitorusersee/则可以不携带jwt token信息即可访问到"anonymous user"数据

控制使用jwt的登录接口返回的数据

正常使用jwt的登录接口登录成功后只会返回一个token信息,可以配置返回其它信息
查看restframework-jwt配置文件settings.py源码:USER_SETTINGS = getattr(settings, 'JWT_AUTH', None)说明我们要让jwt使用我们项目的配置,则需配置JWT_AUTH={…}'JWT_RESPONSE_PAYLOAD_HANDLER':'rest_framework_jwt.utils.jwt_response_payload_handler',此源码为jwt内部配置的使用jwt_response_payload_handler方法返回登录成功后的信息,点进去看该方法源码可得:内部只返回了一个token信息,且该方法会自动传user和request
∴我们可以重写jwt_response_payload_handler方法并在settings.py中配置来控制登录成功后返回的数据utls.my_jwt_response_payload_handler.pydef my_jwt_response_payload_handler(token, user=None, request=None):return {'token': token,'msg':'登录成功','name':user.username,'method':request.method}settings.pyJWT_AUTH = {'JWT_RESPONSE_PAYLOAD_HANDLER':'utils.my_jwt_response_payload_handler.my_jwt_response_payload_handler',}之后登录jwt的接口成功后返回的就是我们控制的数据啦

自定制jwt认证类

发现jwt的内置认证类JSONWebTokenAuthentication是继承BaseJSONWebTokenAuthentication类的

我们要重写认证类,需继承BaseJSONWebTokenAuthentication,然后重写authenticate方法。但其实重写authenticate方法思路和逻辑和原方法差不多

-utls.MyJSONWebTokenAuthentication.py

from rest_framework_jwt.authentication import BaseJSONWebTokenAuthentication
from rest_framework_jwt.utils import jwt_decode_handler
import jwt # 内置模块,内含许多jwt异常/错误
from rest_framework.exceptions import AuthenticationFailedclass MyJSONWebTokenAuthentication(BaseJSONWebTokenAuthentication): # 继承基类def authenticate(self, request):jwt_value = request.META.get('HTTP_AUTHORIZATION')# 这样就直接带token就行了,不用JWT空格tokenif jwt_value:try:payload = jwt_decode_handler(jwt_value)# 还是使用的jwt的通过jwt token反解出payload的方法,需提前导入# print(payload) #{'user_id': 1, 'email': '', 'username': 'weer',}# payload就是用户信息的字典except jwt.ExpiredSignature:raise AuthenticationFailed('签证过期')except jwt.DecodeError:raise AuthenticationFailed('签证错误')except jwt.InvalidTokenError:raise AuthenticationFailed('用户非法')except Exception as e:raise AuthenticationFailed(str(e))else:raise AuthenticationFailed('未携带认证信息')# 调用BaseJSONWebTokenAuthentication提供的通过payload解出user对象的方法-源码user = self.authenticate_credentials(payload)# 也可自己设置方法-两种# 第一种:查数据库# from app01 import models# user = models.User.objects.filter(pk=payload.get('user_id'))# 第二种:不走数据库,创建临时对象# from app01 import models# user = models.User(id=payload.get('user_id'), username= payload.get('username'))return user, jwt_value

-views.py

# jwt自定义认证类
from utils.MyJSONWebTokenAuthentication import MyJSONWebTokenAuthentication
class OnlyLoginUserCanSeeAPIView2(APIView):# 限制登录用户才能访问需要配以下两个内置类authentication_classes = [MyJSONWebTokenAuthentication,]permission_classes = [IsAuthenticated,]def get(self, request):return Response('login user2')

-urls.py

# jwt自定制认证类
path('loginusersee2/', views.OnlyLoginUserCanSeeAPIView2.as_view()),

也可继承BaseAuthentication写

使用jwt自动签发token+多方式登录

这里我们设置登录时不仅可由用户名密码登录,也可由电话或邮箱、密码登录,并对不同的登录方式自动签发token返回

utls.myser1.pyfrom rest_framework import serializersfrom app01 import modelsimport refrom rest_framework.exceptions import ValidationErrorfrom rest_framework_jwt.utils import jwt_payload_handler,jwt_encode_handlerclass ManyWaysToLoginSerializers(serializers.ModelSerializer):username = serializers.CharField() # 重覆盖原来的username字段,否则原来的校验都过不了,会报原用户已存在的错,因为原username字段设置了unique=True,post请求认为你是保存数据就会报错          或命名为其它名字class Meta:model = models.Userfields = ['username', 'password']# 在这写多种方式登录的校验逻辑# 要校验两个,用全局钩子def validate(self, attrs):# print(self.context) #{'test': 'test'}由视图传来,检验context这个交互媒介# 获取登录数据username = attrs.get('username')password = attrs.get('password')# 判断登录方式是哪种分别处理,用正则匹配,获取用户对象if re.match('^1[3-9][0-9]{9}$',username):user_obj = models.User.objects.filter(phone=username).first()elif re.match('[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+',username):user_obj = models.User.objects.filter(email=username).first()else:user_obj = models.User.objects.filter(username=username).first()# 校验用户是否存在及密码if user_obj:# 校验密码if user_obj.check_password(password): # 密码是密文,用check_password# 签发tokenpayload = jwt_payload_handler(user_obj)token = jwt_encode_handler(payload)self.context['username'] = user_obj.username# 如何把token传到视图函数中呢?→self.context['token'] = token# 全局钩子校验完后必须返回所有数据return attrselse:raise ValidationError('密码错误')else:raise ValidationError('用户不存在')
views.py# from rest_framework.viewsets import ViewSetMixin# from rest_framework.views import APIViewfrom rest_framework.viewsets import ViewSetfrom utils import myser1# class ManyWaysToLogin(ViewSetMixin,APIView):等价class ManyWaysToLogin(ViewSet):def login(self, request):# 1 需要一个序列化类,生成序列化对象ser_obj = myser1.ManyWaysToLoginSerializers(data=request.data, context={'test':'test'})# 2 在序列化类中写多方式登录校验逻辑# 3 调用序列化累is_validated方法,校验过了才往下执行ser_obj.is_valid(raise_exception=True)# 4 获取tokentoken = ser_obj.context.get('token')# 5 返回数据return Response({'status':100, 'user':ser_obj.context.get('username'), 'msg':'登录成功', 'token':token})
urls.pypath('manywayslogin/', views.ManyWaysToLogin.as_view({'post':'login'})),

此时利用post发送post请求,用三种方式都能登录了

查看序列化基类源码(BaseSerializer),里面初始化__init__时就有一参数

    def __init__(self, instance=None, data=empty, **kwargs):

        self.instance = instance

        if data is not empty:

            self.initial_data = data

        self.partial = kwargs.pop('partial', False)

        self._context = kwargs.pop('context', {})

        kwargs.pop('many', None)

        super().__init__(**kwargs)

就是它提供了context,我们可以利用它作为媒介在里面放参数供序列化类和视图之间交互数据

如myser1.py中打印的{'test':'test'}就是由views.py中传来,views.py中的token就是myser1.py中传来

配置jwt token的过期时间

drf-jwt的settings.py中配置的过期时间是300s,要想更改,在我们项目的settings.py中配置如下:
import datetime
JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 配置jwt token过期时间为7天
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/128939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】计算机网络和因特网

一.基本术语介绍 端系统通过通信链路(communication link)和分组交换机(packet switch)连接到一起,连接这些端系统和分组交换机的物理媒体包括:同轴电缆,铜线,光纤和无线电频谱。而…

人工智能基础_机器学习014_BGD批量梯度下降公式更新_进一步推导_SGD随机梯度下降和MBGD小批量梯度下降公式进一步推导---人工智能工作笔记0054

然后我们先来看BGD批量梯度下降,可以看到这里,其实这个公式来源于 梯度下降的公式对吧,其实就是对原始梯度下降公式求偏导以后的梯度下降公式,然后 使用所有样本进行梯度下降得来的,可以看到* 1/n 其实就是求了一个平均数对吧.所有样本的平均数. 然后我们看,我们这里* 1/n那么…

Istio实战(十二)-Istio 延长自签发证书的有效期

因为历史原因,Istio 的自签发证书只有一年的有效期。如果你选择使用 Istio 的自签发证书,就需要在它们过期之前订好计划进行根证书的更迭。根证书过期可能会导致集群范围内的意外中断。 我们认为每年更换根证书和密钥是一个安全方面的最佳实践,我们会在后续内容中介绍如何完…

启用NTP服务解决Linux系统时间与北京时间不同步问题

一、背景 1、服务器的Linux版本为Linux version 4.18.0-348.7.1.el8_5.x86_64 (mockbuildkbuilder.bsys.centos.org) (gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)) #1 SMP Wed Dec 22 13:25:12 UTC 2021 2、NTP即Network Time Protocol(网络时间协议&am…

webpack 与 grunt、gulp 的不同?

结论先行: Webpack、Grunt 和 Gulp 都是前端开发中常用的构建工具,但是 Webpack 是基于模块化打包的工具,并支持模块化开发。而 Grunt 和 Gulp 都是基于任务的构建工具,自动执行指定的任务,但不支持模块化开发。 1、相…

JavaScript设计模式之适配器模式

一般为了解决不兼容的问题,把一个类的接口换成我们想要的接口,类似于转换器。 举个例子: 我想听歌的时候,我发现我没带耳机,我的手机是 iphone 的,而现在我只有一个 Type-C 的耳机,为了能够听歌…

Unity3D与iOS的交互 简单版开箱即用

本文适合的情况如下: Unity客户端人员 与 IOS端研发人员合作的情况 目录 From U3D to iOS 实现原理 1.unity工程目录创建2个文件 NativeCallProxy.m、NativeCallProxy.h 并且放到Unity工程目录Plugins/iOS/unity_ios_plus目录下 2.创建C#调用脚本 定义对应.mm脚…

sql常用语句笔记

增&#xff1a;INSERT INTO <表名> &#xff08;列名&#xff09;VALUES (值) (1)增加&#xff1a; 新增 INSERT INTO 表名 &#xff08;列&#xff09;values (值)&#xff0c;值一一对应列的位置&#xff0c;没写的列值会自动填入null或默认值 eg: INSERT INTO student…

海康监控摄像机和录像机接入LiveMedia GB28181平台实现远程调取监控视频

海康威视各种型号监控摄像头或硬盘录像机&#xff08;NVR/HVR&#xff09;接入LiveMedia GB28181平台配置过程都非常简单明了&#xff0c;但有些细节需要注意&#xff0c;避免走弯路。 1、基本要求 (1) 网络要求 总体来说&#xff0c;只要监控设备和GB28181平台的网络是连通…

centos9 stream 下 rabbitmq高可用集群搭建及使用

RabbitMQ是一种常用的消息队列系统&#xff0c;可以快速搭建一个高可用的集群环境&#xff0c;以提高系统的弹性和可靠性。下面是搭建RabbitMQ集群的步骤&#xff1a; 基于centos9 stream系统 1. 安装Erlang和RabbitMQ 首先需要在所有节点上安装Erlang和RabbitMQ。建议使用官…

7个UI设计必备课程,小白必看!

无论你是想提高技能的资深UI设计师还是网站开发人员&#xff0c;又或者是刚转行不久的UI设计新手&#xff0c;学习UI设计课程都会让你做出更美观、更有影响力的UI界面设计作品。现在网上有很多网上的UI设计课程。通过这些课程&#xff0c;你可以自己学习、掌握一些UI设计的基础…

【Jmeter】生成html格式接口自动化测试报告

jmeter自带执行结果查看的插件&#xff0c;但是需要在jmeter工具中才能查看&#xff0c;如果要向领导提交测试结果&#xff0c;不够方便直观。 笔者刚做了这方面的尝试&#xff0c;总结出来分享给大家。 这里需要用到ant来执行测试用例并生成HTML格式测试报告。 一、ant下载安…

k8s之集群调度

目录 调度 工作机制 调度过程 调度算法 优先级 指定调度节点 调度 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令…

linux远程桌面管理工具xrdp

一、概述 我们知道&#xff0c;我们日常通过vnc来远程管理linux图形界面&#xff0c;今天分享一工具Xrdp&#xff0c;它是一个开源工具&#xff0c;允许用户通过Windows RDP访问Linux远程桌面。 除了Windows RDP之外&#xff0c;xrdp工具还接受来自其他RDP客户端的连接&#xf…

C# Winform串口助手

界面设置 修改控件name属性 了解SerialPort类 实现串口的初始化&#xff0c;开关 创建虚拟串口 namespace 串口助手 {public partial class Form1 : Form{public Form1(){InitializeComponent();}private void Form1_Load(object sender, EventArgs e){//在设计页面已经预先…

Map和Set(JAVA)

本篇文章建议在了解了哈希表和二叉搜索树后食用更佳。 链接: 二叉搜索树 和 哈希表 (JAVA) Map和Set都是一种专门用来进行搜索的容器或者数据结构&#xff0c;其搜索的效率与其具体的实例化子类有关。 Map接口 Map是一个接口&#xff0c;不能直接实例化对象&#xff0c;如果…

vue3源码地址

https://github.com/vuejs/core

API接口宝藏:免费好用资源分享

天气预警&#xff1a;获取指定城市当前生效中的各类天气预警&#xff0c;如寒潮蓝色预警信号&#xff0c;或一次性拉取全国所有生效中的天气预警。预警数据来自国家预警中心。 IP归属地-IPv4区县级&#xff1a;根据IP地址查询归属地信息&#xff0c;包含43亿全量IPv4&#xff…

【css3】涟漪动画

效果展示 dom代码 <div class"mapSelfTitle66"><div></div> </div> 样式代码 .mapSelfTitle66{width:120px;height:60px;position: relative;&>div{width:100%;height:100%;background: url("~/assets/images/video_show/err…

javaee实验:搭建maven+spring boot开发环境,开发“Hello,Spring Boot”应用

目录 mavenspringboot实验目的实验内容环境的搭建 在开发中&#xff0c;maven和spring都是非常常用、非常重要的管理工具和框架&#xff0c;今天就在这里使用idea进行环境的搭建和创建第一个spring程序 maven 1.1maven是一个跨平台的项目管理工具&#xff08;主要管理jar包&am…