jwt认证课件讲解
JWT
基本概念
在用户登录后,我们需要在不同请求之间记录用户的登录状态,常用方式一般有三种:Cookie,Session和Token。
这里我们使用第三种Token令牌方式来实现认证鉴权,采用Json Web Token认证机制(简称:jwt)。
Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
jwt官网:https://jwt.io/
jwt规范:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-json-web-token
JWT的构成

JWT就一段由三段信息构成的字符串,将这三段信息文本用.拼接一起就构成的。就像这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).
jwtToken = f"{header}.{payload}.{signature}"
header
jwt的头部承载两部分信息:
- typ: type的缩写,声明token的类型,值一般可以是
JWT或Bear。 - alg: algorithm的缩写,声明token的第三方部分(签证)的加密算法,通常直接使用
HMAC SHA256
完整的头部就像下面这样的JSON:
{"typ": "Bear","alg": "HS256"
}
然后将头部进行base64.b64urlencode()编码,构成了第一部分头部。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
python代码实现过程:
import base64,json
data = {'typ': 'JWT','alg': 'HS256'
}header = base64.b64encode(json.dumps(data).encode()).decode()# 各个语言中都有base64加密解密的功能,所以我们jwt为了安全,需要配合第三段加密
payload
载荷(payload)就是jwt存放有效信息的部分。这个名字像是特指飞机上承载的货仓,这些有效信息包含三种不同类型的数据:
- 标准声明
- 公共声明
- 私有声明
标准声明 (官方提出建议但不强制使用) :
-
iss: jwt签发者
-
sub: jwt所面向的用户
-
aud: 接收jwt的一方
-
exp: jwt的过期时间,这个过期时间必须要大于签发时间
-
nbf: 定义在什么时间之后,该jwt可以正常使用。
-
iat: jwt的签发时间
-
jti: jwt的唯一身份标识,主要用来作为一次性token,往往采用UUID字符串或随机字符串来充当。
以上是JWT规范中提供的7个官方字段,开发者根据自己的业务进行选用。
公共声明:公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。但不建议添加敏感信息,因为该部分在客户端直接可以查看。
私有声明:私有声明是服务端和客户端所共同定义的声明,一般使用类似ace算法进行非对称加密和解密的,意味着该部分信息可以归类为明文信息。
定义一个payload,json格式的数据:
{"sub": "1234567890", // 时间戳"exp": "3422335555", // 时间戳"name": "John Doe","admin": true,
}
然后将其进行base64.b64encode() 编码,得到JWT的第二部分。
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
python代码实现过程:
import base64,json
data = {"sub": "1234567890","exp": "3422335555","name": "John Doe","admin": True,"info": "232323ssdgerere3335dssss"
}payload = base64.b64encode(json.dumps(data).encode()).decode()# 各个语言中都有base64编码和解码,所以我们jwt为了安全,需要配合第三段签证来进行加密保证jwt不会被人篡改。
signature
JWT的第三部分叫签证信息,主要用于辨真伪,防篡改。签证信息使用加密算法生成,公式:
secret_key = "秘钥" # 只保存服务端,不能外泄
signature = SHA256(base64.b64encode(header) + "." +base64.b64encode(payload),secret_key)
python代码实现过程:
import sys, json, base64, time, hmacif __name__ == '__main__':# 头部data = {'typ': 'JWT', 'alg': 'HS256'}header = base64.b64encode(json.dumps(data).encode()).decode()# 载荷data = {"sub": "1234567890", "exp": "3422335555", "name": "John Doe", "admin": True,"info": "232323ssdgerere3335dssss"}payload = base64.b64encode(json.dumps(data).encode()).decode()# 签证,生成jwt token 提供给客户端# from django.conf import settings# secret = settings.SECRET_KEYsecret = 'django-insecure-(_+qtd5edmhm%2rdsg+qc3wi@s_k*3cbk-+k2gpg3@qx)z6r+p'sign = base64.b64encode(f"{header}.{payload}".encode())signature = base64.b64encode(hmac.digest(secret.encode(), sign, digest="sha256")).decode()jwt = f"f{header}.{payload}.{signature}"print(jwt)# 将这三部分用`.`连接成一个完整的字符串,构成了最终的jwt:# feyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJleHAiOiAiMzQyMjMzNTU1NSIsICJuYW1lIjogIkpvaG4gRG9lIiwgImFkbWluIjogdHJ1ZSwgImluZm8iOiAiMjMyMzIzc3NkZ2VyZXJlMzMzNWRzc3NzIn0=.3OnGXAx5wWA5AjxyewICSn5Hirz1tXfzxOc4tns4elM=
注意:
secret是保存在服务器端的,jwt的签发生成代码也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,
所以它应该是服务端的私钥,在任何场景下都不应该流露出去,而且应该在每次服务端更新维护后及时更新。
一旦第三方得知这个secret, 那就意味着他们绕过服务端伪造jwt了。
优缺点
优点:
- 实现分布式集群的单点登陆非常方便
- Token实际保存在客户端,所以我们可以分担服务端的存储压力。
- jwt不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。
缺点:
- jwt保存在客户端,我们服务端只认jwt,不识别客户端。
- 解决方案1. 设置客户端唯一登陆
- 解决方案2. 绑定客户端的标记符和IP,机器码
- jwt可以设置过期时间,但是因为jwt保存在了客户端,所以对于过期时间不好调整,一旦签发不可控。
- 解决方案1:设置短有效期,例如:30分、15分钟、10分钟之类。
- 解决方案2:生成jwt的时候,提供给客户端之前先在内存(一般使用内存数据库redis,而不是变量)备份jwt,每次用户访问需要登录身份数据时,把token去内存中验证一样。
使用jwt实现认证流程
所谓的认证流程,实际上就是用户登录的过程。

python代码实现认证流程,代码:
import sys, json, base64, time, hmacif __name__ == '__main__':# 模拟客户端提交的tokenclient_token = "feyJ0eXAiOiAiSldUIiwgImFsZyI6ICJIUzI1NiJ9.eyJzdWIiOiAiMTIzNDU2Nzg5MCIsICJleHAiOiAiMzQyMjMzNTU1NSIsICJuYW1lIjogIkpvaG4gRG9lIiwgImFkbWluIjogdHJ1ZSwgImluZm8iOiAiMjMyMzIzc3NkZ2VyZXJlMzMzNWRzc3NzIn0=.3OnGXAx5wWA5AjxyewICSn5Hirz1tXfzxOc4tns4elM="# 把客户端提交的token分割成三段:头部、载荷、签证header, payload, signature = client_token.split(".")# 验证是否过期了,先基于base64,接着使用json解码,提供载荷中的过期时间进行比较payload_data = json.loads(base64.b64decode(payload.encode()))exp = int(payload_data.get("exp", 0))now = int(time.time())if exp < now:print("token已经过期!")sys.exit() # 退出程序,实际开发中,应该时响应代码给客户端,不会继续往下执行了。secret = "django-insecure-(_+qtd5edmhm%2rdsg+qc3wi@s_k*3cbk-+k2gpg3@qx)z6r+p"# 与生成token时一样的秘钥和数据,再次生成一个签证new_signature = hmac.digest(secret.encode(), sign, digest="sha256")# 拿客户端提交上面的token中的签证进行base64解码得到原始的签证signature = base64.b64decode(signature)# 通过compare_digest比较两者是否吻合if hmac.compare_digest(signature, new_signature):print("认证通过")else:print("认证失败,token被串改!")
基本使用
开发中除非找不到,否则我们可以直接使用第三方已经开源的模块来完成相关的功能。大部分要求使用第三方模块是必须star数量>150。
依赖库安装
# python-jose 用于生成和检验JWT令牌
pip install jwt
pip install python-jose
JWT基本使用
生成一个随机的密钥,用于对JWT令牌进行签名加密的。终端执行命令如下:
openssl rand -hex 32
# eac77e4e9a9a767b792779132e84ea37b1f4c31bec56714607f617a3fbdfbd53
创建JWT需要的相关配置项,settings.py,代码:
# 加密数据所使用的秘钥[盐值]
SECRET_KEY = "eac77e4e9a9a767b792779132e84ea37b1f4c31bec56714607f617a3fbdfbd53"
# 设定JWT令牌签名算法
ALGORITHM = "HS256"
# 设置令牌过期时间变量(单位:秒)
ACCESS_TOKEN_EXPIRE_MINUTES = 30 * 60
创建JWT工具类,utils.py,代码:
from typing import Optional
from datetime import timedelta, datetime
import settings
from jose import jwt
import uuidclass JWT(object):JWTError = jwt.JWTErrorExpiredSignatureError = jwt.ExpiredSignatureErrordef create_token(self, data: dict, expire_time: Optional[timedelta] = None):"""生成Token:param data: 需要进行JWT令牌加密的用户信息(解密的时候会用到):param expire_time: 令牌有效期,单位:秒:return: token"""now_time = datetime.utcnow()if expire_time:expire = now_time + timedelta(seconds=expire_time)else:expire = now_time + timedelta(seconds=settings.ACCESS_TOKEN_EXPIRE_TIME)payload = {"exp": expire,"iat": now_time,"nbf": now_time,"jti": str(uuid.uuid4())}payload.update(data)token = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)return tokendef verify_token(self, token: str) -> dict:"""验证token:param token: 客户端发送过来的token:return: 返回用户信息"""payload = jwt.decode(token, settings.SECRET_KEY, algorithms=settings.ALGORITHM)return payloadif __name__ == '__main__':"""密码加密与验证"""# hashing = Hashing()# hashed_pwd = hashing.hash("123456")# print(hashed_pwd) # 加密后要保存到数据库中的哈希串# # 把原密码和加密后的哈希串进行配对,验证通过则返回结果为True# ret = hashing.verify("123456", hashed_pwd)# print(ret)"""JWT"""jwt_tool = JWT()try:# 正确使用token = jwt_tool.create_token({'username': 'admin', 'sex': True})print(token)data = jwt_tool.verify_token(token)print(data)# # 因为Token过期导致验证失败# token = jwt_tool.create_token({'username': 'admin', 'sex': True}, -300)# print(token)# data = jwt_tool.verify_token(token)# print(data)# # 因为Token被串改导致验证失败# token = jwt_tool.create_token({'username': 'admin', 'sex': True})# print(token)# data = jwt_tool.verify_token(token[:-1])# print(data)except (jwt_tool.ExpiredSignatureError, jwt_tool.JWTError) as e:print("验证失败,", e)
基于自定义中间件创建JWT中间件实现用户身份认证
async def jwt_middleware(request: Request, call_next):try:token: str = request.headers["Authorization"].split()[1]payload = jwt_took.verify(token)id: str = payload.get("id")# 查询数据库,是否存在当前用户user = await models.User.filter(id=id).first(id)if user is None:raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials")request.user = userexcept (jwt_tool.ExpiredSignatureError, jwt_tool.JWTError):raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail="Invalid authentication credentials")response = await call_next(request)return response
注册JWT中间件,代码:
app.add_middleware(jwt_middleware)
相关文章:
jwt认证课件讲解
JWT 基本概念 在用户登录后,我们需要在不同请求之间记录用户的登录状态,常用方式一般有三种:Cookie,Session和Token。 这里我们使用第三种Token令牌方式来实现认证鉴权,采用Json Web Token认证机制(简称…...
【判断推理】逻辑基础
1.1 命题 用语言、符号或者式子表达的,可以判断真假的陈述句称为命题,一般写为 若p,则q 真命题:判断为真的语句假命题:判断为假的语句 eg1:小张是中国人(若是小张,则是中国人&#…...
AcWing 655:天数转换 ← 整除、求余
【题目来源】https://www.acwing.com/problem/content/657/【题目描述】 读取对应于一个人的年龄(以天为单位)的整数值,并转化为年,月和日表示方式输出,年、月、日分别对应 ano(s), mes(es), dia(s)。 注意:…...
【解决办法】git clone报错unable to access ‘xxx‘: SSL certificate problem:
使用git clone 时报错unable to access xxx: SSL certificate problem: 这个报错通常是由于SSL证书问题引起的。通常可以按照以下步骤进行排查: 检查网络连接:确保你的网络连接正常,可以访问互联网。尝试使用其他网站或工具测试网络连接是否正…...
算法笔记(十三)——BFS 解决最短路问题
文章目录 迷宫中离入口最近的出口最小基因变化单词接龙为高尔夫比赛砍树 BFS 解决最短路问题 BFS(广度优先搜索) 是解决最短路径问题的一种常见算法。在这种情况下,我们通常使用BFS来查找从一个起始点到目标点的最短路径。 迷宫中离入口最近的出口 题目:…...
Android 简单实现联系人列表+字母索引联动效果
效果如上图。 Main Ideas 左右两个列表左列表展示人员数据,含有姓氏首字母的 header item右列表是一个全由姓氏首字母组成的索引列表,点击某个item,展示一个气泡组件(它会自动延时关闭), 左列表滚动并显示与点击的索引列表item …...
自动驾驶-问题笔记-待解决
参考线的平滑方法 参考线平滑算法主要有三种: 离散点平滑;螺旋曲线平滑;多项式平滑; 参考链接:参考线平滑 对于平滑方法,一直不太理解平滑、拟合以及滤波三者的作用与区别; 规划的起点&#x…...
在掌控板中加载人教版信息科技教学指南中的educore库
掌控板中加载educore库 人教信息科技数字资源平台(https://ebook.mypep.cn/free)中的《信息科技教学指南硬件编程代码说明》文件中提到“本程序说明主要供教学参考。需要可编程主控板须支持运行MicroPython 脚本程序。希望有更多的主控板在固件中支持ed…...
关于CSS Grid布局
关于CSS Grid布局 实际效果参考 参考代码 <template><view class"baseInfo"><up-image class"cover" height"160rpx" width"120rpx" :src"bookInfo.cover"><template #error><view style"…...
初始爬虫12(反爬与反反爬)
学到这里,已经可以开始实战项目了,多去爬虫,了解熟悉反爬,然后自己总结出一套方法怎么做。 1.服务器反爬的原因 服务器反爬的原因 总结: 1.爬虫占总PV较高,浪费资源 2.资源被批量抓走,丧失竞争力…...
成像基础 -- 最大对焦清晰的物距计算
最大对焦清晰的物距计算 1. 基本概念 最大对焦清晰的物距通常与景深(Depth of Field, DOF)相关,尤其是无穷远处的物体可以被清晰对焦到的距离,称为超焦距(Hyperfocal Distance)。通过计算超焦距ÿ…...
win10服务器启动且未登录时自动启动程序
场景:公司服务器安装了几个程序,当服务器断电重启之后希望程序能自动打开,而不需要手动登录服务器打开。 因为软件是自己开发的所以安全方面这里没有考虑。 1.打开服务器管理器,点击工具,选择任务计划程序 2.在任务计…...
算法专题四: 前缀和
目录 1. 前缀和2. 二维前缀和3. 寻找数组的中心下标4. 除自身以外数组的乘积5. 和为k的子数组6. 和可被K整除的子数组7. 连续数组8. 矩阵区域和 博客主页:酷酷学!!! 感谢关注~ 1. 前缀和 算法思路: 根据题意, 创建一个前缀和数组, dp[i] dp[i -1] arr[i], 再使用前缀和数组,…...
【Linux】基础IO(文件描述符、缓冲区、重定向)
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm1010.2135.3001.5343🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html 目录 前言 C文件IO相关操作 系统文件I/O open open函数返回值 文件描述符fd re…...
一篇文章快速学会docker容器技术
目录 一、Docker简介及部署方法 1.1Docker简介 1.1.1什么是docker 1.1.2 docker在企业中的应用场景 1.1.3 docker与虚拟化的对比 1.1.4 docker的优势 二 、部署docker 2.1 容器工作方法 2.2 部署第一个容器 2.2.1 配置软件仓库 2.2.2 安装docker-ce并启动服务 2.2.…...
【MySQL】使用 JDBC 连接数据库
文章目录 前言1. 认识 JDBC1.1 概念1.2 好处 2. 使用 JDBC2.1 安装数据驱动包2.2 把 jar 包导入到项目中2.3 代码编写2.4 测试结果 3. 代码优化4. 源码展示结语 前言 在 MySQL 系列中,我们介绍了很多内容,包括但不限于建库建表,增删查改等等…...
数据结构与算法笔记:概念与leetcode练习题
1、数组Array 时间复杂度 数组访问:O(1) 数组搜索:O(N) 数组插入:O(N) 数组删除:O(N) 特点 适合读,不适合写 数组常用操作 # 1、创建数组 a [] # 2、尾部添加元素 a.append(1) a.append(2) a.append(3) # 3、…...
十大时间序列预测模型
目录 1. 自回归模型 原理 核心公式 推导过程: 完整案例 2. 移动平均模型 原理 核心公式 推导过程: 完整案例 3. 自回归移动平均模型 原理 核心公式 推导过程: 完整案例 4. 自回归积分移动平均模型 原理 核心公式 推导过程 完整案例 5. 季节性自回归积分…...
G2O 通过工厂函数类 OptimizationAlgorithmFactory 来生成固定搭配的优化算法
OptimizationAlgorithmFactory 类位于 optimization_algorithm_factory.h //***g2o源码 g2o/g2o/core/optimization_algorithm_factory.h ***// /*** \brief create solvers based on their short name** Factory to allocate solvers based on their short name.* The Factor…...
手机USB连接不显示内部设备,设备管理器显示“MTP”感叹号,解决方案
进入小米驱动下载界面,等小米驱动下载完成后,解压此驱动文件压缩包。 5、小米USB驱动安装方法:右击“计算机”,从弹出的右键菜单中选择“管理”项进入。 6、在打开的“计算机管理”界面中,展开“设备管理器”项&…...
【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
Vue ③-生命周期 || 脚手架
生命周期 思考:什么时候可以发送初始化渲染请求?(越早越好) 什么时候可以开始操作dom?(至少dom得渲染出来) Vue生命周期: 一个Vue实例从 创建 到 销毁 的整个过程。 生命周期四个…...
【UE5 C++】通过文件对话框获取选择文件的路径
目录 效果 步骤 源码 效果 步骤 1. 在“xxx.Build.cs”中添加需要使用的模块 ,这里主要使用“DesktopPlatform”模块 2. 添加后闭UE编辑器,右键点击 .uproject 文件,选择 "Generate Visual Studio project files",重…...
Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
