JWT

news2024/11/26 17:51:24

目录

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.py
	def my_jwt_response_payload_handler(token, user=None, request=None):
	    return {
	        'token': token,
	        'msg':'登录成功',
	        'name':user.username,
	        'method':request.method
	    }
	settings.py
	JWT_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 AuthenticationFailed

class MyJSONWebTokenAuthentication(BaseJSONWebTokenAuthentication): # 继承基类
    def authenticate(self, request):
        jwt_value = request.META.get('HTTP_AUTHORIZATION')
        # 这样就直接带token就行了,不用JWT空格token
        if 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.py
	from rest_framework import serializers
	from app01 import models
	import re
	from rest_framework.exceptions import ValidationError
	from rest_framework_jwt.utils import jwt_payload_handler,jwt_encode_handler

	class ManyWaysToLoginSerializers(serializers.ModelSerializer):
	    username = serializers.CharField() # 重覆盖原来的username字段,否则原来的校验都过不了,会报原用户已存在的错,因为原username字段设置了unique=True,post请求认为你是保存数据就会报错          或命名为其它名字
	    class Meta:
	        model = models.User
	        fields = ['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
	                # 签发token
	                payload = jwt_payload_handler(user_obj)
	                token = jwt_encode_handler(payload)
	                self.context['username'] = user_obj.username
	                # 如何把token传到视图函数中呢?→
	                self.context['token'] = token
	                # 全局钩子校验完后必须返回所有数据
	                return attrs
	            else:
	                raise ValidationError('密码错误')
	        else:
	            raise ValidationError('用户不存在')
views.py
	# from rest_framework.viewsets import ViewSetMixin
	# from rest_framework.views import APIView
	from rest_framework.viewsets import ViewSet
	from 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 获取token
	        token = ser_obj.context.get('token')
	        # 5 返回数据
	        return Response({'status':100, 'user':ser_obj.context.get('username'), 'msg':'登录成功', 'token':token})
urls.py
    path('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.coloradmin.cn/o/1167140.html

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

相关文章

Sui zkSend,创建链接可直接发送SUI,快来体验吧

基于zkLogin和Sui的本机密码学构建,zkSend允许用户创建包含特定数量SUI的链接。该链接可以通过任何消息客户端发送,例如电子邮件和直接消息,甚至可以在媒体上转换成QR码。 使用zkSend发送SUI比复制和粘贴钱包地址,然后授权token转…

电脑技巧:WmiPrvSE.exe是什么进程?WMI Provider Host占用很高CPU的原因及解决办法

目录 一、WmiPrvSE.exe是什么进程? 二、WMI Provider Host简介 三、WMI Provider Host为什么占用很高CPU? 四、WMI Provider Host是否可禁用? 一、WmiPrvSE.exe是什么进程? WMI Provider Host (WmiPrvSE.exe&#…

RT-Thread系统使用常见问题处理记录

1.使用telnet连接系统时发送help指令显示不全的问题。 原因:telnet发送缓存太小。 解决办法:更改agile_telnet软件包里Set agile_telnet tx buffer size的大小。 2.使用Paho MQTT软件包过一段时间报错hard fault on thread: mqtt0 解决办法&#xff1…

ElementuiPlus的table组件实现行拖动与列拖动

借助了插件sortablejs。这种方法只适合做非树状table。如果想实现树状table&#xff0c;并且可拖动。可以试一下aggridVue3这个插件 <template><div class"draggable" style"padding: 20px"><el-table row-key"id" :data"t…

linux——网络套接字编程

目录 一.简单了解TCP和UDP协议 二.网络字节序 三.socket常见的编程接口 1.介绍接口 2.sockaddr结构 四.简单的UDP网络程序 1.recvfrom和sendto 2.server.cc 3.client.cc 五.简单的TCP通信 1.client.cc 2.server.cc 一.简单了解TCP和UDP协议 此处我们先对TCP(Transm…

Unity在Project右键点击物体之后获取到点击物体的名称

Unity在Project右键点击物体之后获取到点击物体的名称 描述&#xff1a; 在Unity的Project右键点击物体之后选择对应的菜单选项点击之后打印出物体的名称 注意事项 如果获取到文件或者预制体需要传递objcet类型&#xff0c;然后使用 GameObject.Instantiate((GameObject)se…

人工智能-深度学习计算:层和块

我们关注的是具有单一输出的线性模型。 在这里&#xff0c;整个模型只有一个输出。 注意&#xff0c;单个神经网络 &#xff08;1&#xff09;接受一些输入&#xff1b; &#xff08;2&#xff09;生成相应的标量输出&#xff1b; &#xff08;3&#xff09;具有一组相关 参数…

BUUCTF RSA4

BUUCTF RSA4 下载题目&#xff0c;可见文件给出了3组n和c N 331310324212000030020214312244232222400142410423413104441140203003243002104333214202031202212403400220031202142322434104143104244241214204444443323000244130122022422310201104411044030113302323014101…

基于仿真的飞机ICD工具测试

机载电子系统是飞机完成飞行任务的核心保障之一。从1949年新中国建立至今70余年的发展过程中&#xff0c;随着我国在航空航天领域的投资逐年增多&#xff0c;机载电子系统大致经历了四个发展过程阶段&#xff0c;按照出现的先后顺序进行排序&#xff0c;分别为&#xff1a; 1、…

MyBatis实现多表映射、分页显示、逆向工程

目录 一、MyBatis实现多表映射 1.1 实体类设计 1.2 一对一关系实现案例 1.3 对多配置实现案例 1.4 设置自动映射与n张表关联映射 二、MyBatis实现分页功能 2.1 mybatis插件工作原理 2.2 引入插件与插件的使用 三、逆向工程插件 3.1 什么是逆向工程 3.2 MyBat…

vue3路由配置

npm下载安装路由 npm install vue-router4 --save新建router目录 新建routes路由表可以分离&#xff0c;也可以写在路由文件下的index.js 想用方式引入&#xff0c;需要新建vite.config.ts文件配置

【Redis】使用Java操作Redis

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《Redis》。&#x1f3af;&#x1f3af; &#x1f4…

文本内容转换成语音播放的工具:Speech Mac

Speech Mac版是一款适用于Mac电脑的语音合成工具。它将macOS语音合成器的所有功能整合到一个易于使用的界面中。通过Speech Mac版&#xff0c;用户可以选择40多种声音和语言&#xff0c;方便地将文本转换为语音。用户可以将文本拖放或粘贴到Speech中&#xff0c;并随时更改语音…

JPA编程使用注解@LastModifiedDate,自动更新修改时间字段

一、背景 jpa编程中&#xff0c;我们一般会设计以下五个字段&#xff1a; 在新增记录的时候&#xff0c;无需对创建时间和修改时间进行赋值。 此外&#xff0c;你还得在类上&#xff0c;增加注解EntityListeners(AuditingEntityListener.class)。 EntityListeners(Auditing…

用中文编程工具给澳大利亚客户定制开发的英文版服装进销存软件应用实例

用中文编程工具给澳大利亚客户定制开发的英文版服装进销存软件应用实例 软件从2016年一直用到现在&#xff0c;而且开的分店也是安装的这个软件&#xff0c;上图是定制打印的格式。 该编程工具不但可以连接硬件&#xff0c;而且可以开发大型的软件。 编程系统化课程总目录及明…

Linux下的IMX6ULL——构建bootloader、内核、文件系统(四)

前言&#xff1a; Linux 平台上有许多开源的嵌入式linux系统构建框架(框架的意思就是工 具)&#xff0c;这些框架极大的方便了开发者进行嵌入式系统的定制化构建&#xff0c;目前比较常 见的有OpenWrt, Buildroot, Yocto,等等。其中Buildroot功能强大&#xff0c;使用 简单&…

CY3-N3的荧光特性Cyanine3 azide星戈瑞

CY3-N3是一种荧光染料&#xff0c;其荧光特性通常取决于其分子结构和环境条件。CY3-N3的激发波长通常位于500到550纳米之间。这表示当暴露在具有适当激发光的条件下时&#xff0c;CY3-N3会吸收光并进入激发态。 CY3-N3的发射波长通常位于550到650纳米之间。这意味着在激发后&a…

Mac连接linux的办法(自带终端和iterm2)

1. 使用Mac自带终端Terminal 1.1 点击右上角的聚焦搜索&#xff0c;再输入终端 1.2 查找linux系统的ip地址 在虚拟机里输入如下命令&#xff0c;找到蓝色区域的就是ip地址 ip addr 如果没有显示ip地址&#xff0c;可以重新安装一下虚拟机&#xff0c;之后确保以太网的连接是打…

竹胶板木板片:多功能建筑模板的理想选择

竹胶板木板片作为一种多功能的建筑模板材料&#xff0c;在各类建筑项目中扮演着重要的角色。其防水、耐磨的特性使其成为庭院、阳台等室外空间制模的理想选择。本文将重点介绍竹胶板木板片的特点以及其在建筑模板领域的应用。 竹胶板木板片由优质的竹材制成&#xff0c;经过胶合…

Java中Deque栈对象的增删查(所有方法详解)

1、Deque栈的增删查方法总结 2、方法增删查 栈顶添加&#xff1a;push、offFirst栈尾添加&#xff1a;add、offer、offerLast栈顶删除&#xff1a;remove、pop、poll、pollFirst栈尾删除&#xff1a;pollLast栈顶查看&#xff1a;peek、peekFirst栈尾查看&#xff1a;peekLast…