jwt认证课件讲解

news2024/12/28 12:47:37

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, hmac

if __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_KEY
    secret = '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了。

优缺点

优点:

  1. 实现分布式集群的单点登陆非常方便
  2. Token实际保存在客户端,所以我们可以分担服务端的存储压力。
  3. jwt不仅可用于认证,还可用于信息交换。善用JWT有助于减少服务器请求数据库的次数,jwt的构成非常简单,字节占用很小,所以它是非常便于传输的。

缺点:

  1. jwt保存在客户端,我们服务端只认jwt,不识别客户端。
    • 解决方案1. 设置客户端唯一登陆
    • 解决方案2. 绑定客户端的标记符和IP,机器码
  2. jwt可以设置过期时间,但是因为jwt保存在了客户端,所以对于过期时间不好调整,一旦签发不可控。
    • 解决方案1:设置短有效期,例如:30分、15分钟、10分钟之类。
    • 解决方案2:生成jwt的时候,提供给客户端之前先在内存(一般使用内存数据库redis,而不是变量)备份jwt,每次用户访问需要登录身份数据时,把token去内存中验证一样。
使用jwt实现认证流程

所谓的认证流程,实际上就是用户登录的过程。

在这里插入图片描述

python代码实现认证流程,代码:

import sys, json, base64, time, hmac

if __name__ == '__main__':
    # 模拟客户端提交的token
    client_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 uuid


class JWT(object):
    JWTError = jwt.JWTError
    ExpiredSignatureError = jwt.ExpiredSignatureError
    def 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 token

    def verify_token(self, token: str) -> dict:
        """
        验证token
        :param token: 客户端发送过来的token
        :return: 返回用户信息
        """
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=settings.ALGORITHM)
        return payload


if __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 = user
    except (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)

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

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

相关文章

继电器原理及应用

目录 前言 继电器 1.继电器配图 2.继电器原理图 3.继电器的使用 1-继电器的使用意义 2-继电器使用场景 继电器的简单使用 1.使用原理及接线 2.使用思考 3.代码实现 总结 前言 我们上节已经简单了解了震动传感器的使用(不懂的直接去看&#xff1a;震动传感器)&#xff…

【单例模式】

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。 一、实现方式 1. 饿汉式 在类加载的时候就创建实例&#xff0c;无论是否使用&#xff0c;实例都会被创建。优点是实现简单&#xff0c;线程安全。缺点是可能造成资源浪费&#xff0c;而程序可能不一定会使用这个实例…

21 基于51单片机的隧道车辆检测系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 以AT89C51单片机为控制核心&#xff0c;实现对隧道环境的监测。采用模块化设计&#xff0c; 共分以下几个功能模块&#xff1a; 单片机最小系统模块、电源模块、气体传感模块、和显示模块等。 通过…

Yocto - 使用Yocto开发嵌入式Linux系统_08 掌握软件包相关功能

Assimilating Packaging Support 本章介绍了解 Poky 和 BitBake 打包相关方面的关键概念。我们将了解支持的二进制包格式、共享状态缓存、包版本控制组件、如何设置和使用二进制包馈以支持我们的开发流程等。 This chapter presents the key concepts for understanding the as…

Pikachu-Sql-Inject - 通过sql进行远程服务器控制(试验)

secure_file_priv是MySQL中的系统变量&#xff0c;用于限制文件的读取和写入。 查看命令&#xff1a; show variables like "secure%" //或者 select secure_file_priv; 1.secure_file_priv NULL &#xff0c;限制文件的读取和写入。 2.secure_file_priv 文件路…

第三届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2024)

目录 重要信息 大会简介 组织单位 大会成员 征稿主题 会议日程 参会方式 重要信息 大会官网&#xff1a;www.icicml.org 大会时间&#xff1a;2024年11月22日-24日 大会地点&#xff1a;中国 深圳 大会简介 第三届图像处理、计算机视觉与机器学…

【含开题报告+文档+PPT+源码】基于SpringBoot的社区家政服务预约系统设计与实现【包运行成功】

开题报告 社区家政服务是满足居民日常生活需求的重要组成部分&#xff0c;在现代社会中发挥着越来越重要的作用。随着城市化进程的不断加速&#xff0c;社区家政服务需求量呈现持续增长的趋势。然而&#xff0c;传统的家政服务模式存在一些问题&#xff0c;如预约流程繁琐、信…

Kafka的基本概念整理

1、Kafka是什么&#xff1f; Kafka是由Scala语言开发的一个多分区、多副本&#xff0c;基于Zookeeper集群协调的系统。 那这个所谓的系统又是什么系统呢&#xff1f; 回答这个问题要从发展的角度来看&#xff1a;起初Kafka的定位是分布式消息系统。但是目前它的定位是一个分布…

【M365运维】在SPO文档库里删除文档时,遇到文档被签出无法删除。

【问题】SPO的存储空间剩的不多了&#xff0c;在清理文档库时&#xff0c;遇到有些文档被签出但用户已经离职&#xff0c;删除文件时报错。 【解决】翻SPO的设置时&#xff0c;看到有“管理没有已签入版本的文件”&#xff0c;在里面获取文件的所有权之后就可以删除了。 具体…

如何使用ipopt进行非线性约束求目标函数最小值(NLP非线性规划)内点法(inner point method)

非线性规划&#xff0c;一般用matlab调用cplex和gurobi了&#xff0c;但这两个一般用于线性规划和二次规划 线性规划LP&#xff0c;二次规划&#xff08;quadratic programming&#xff09;&#xff0c;如果要求更一般的非线性规划IPOT是个很好的选择&#xff0c;求解器很多&a…

点,点间连接的数学构型系统

点&#xff0c;点间连接的数学构型系统 生物神经系统的宇宙时间尺度的进化过程&#xff1a;通过天文数字的生物数量、天文时间尺度的生态系统得自然&#xff08;按自然规则&#xff09;的演化&#xff0c;沉淀出最高级的人脑神经系统&#xff0c;那么其实&#xff0c;这个过程…

C嘎嘎入门篇:类和对象番外(时间类)

前文&#xff1a; 小编在前文讲述了类和对象的一部分内容&#xff0c;其中小编讲述过运算符重载这个概念以及一个时间类&#xff0c;当时小编讲的没有那么细致&#xff0c;下面小编将会讲述时间类来帮助各位读者朋友更好的去理解运算符重载&#xff0c;那么&#xff0c;代码时刻…

MySQL 日志 - Binlog

文章目录 binlog 的格式mysqbinlog 工具SHOW binlog events;binlog 和 redo log 对比 https://dev.mysql.com/doc/refman/8.4/en/binary-log.html binlog 全称 BinaryLog&#xff0c;是 MySQL 数据库中用于记录所有更改数据库状态的事件的日志文件。它主要用于以下几个目的&am…

OBOO鸥柏丨OLED透明触摸查询一体机数字科技触控广告屏技术前沿

吊挂透明OLED触摸屏一体机正成为博物馆数字化展示的“共同奔赴赛道选择&#xff0c;透过透明屏幕看到展示物品的内部结构和细节&#xff0c;GG纯平面触控实现展示查询交互与互动的完美结合。相比传统的商用/工业液晶显示屏机柜&#xff0c;OLED透明触摸屏具有更高的对比度和更广…

解锁 Python 嵌套字典的奥秘:高效操作与实战应用指南

文章目录 前言&#x1f340;一、 什么是 Python 字典&#xff1f;1.1 字典的语法 &#x1f340;二、 字典的基本操作2.1 字典的创建2.2 访问字典中的值2.3 添加或修改键值对2.4 删除字典中的键值对 &#x1f340;三、 字典的遍历操作3.1 遍历字典的键3.2 遍历字典的值3.3 同时遍…

UGUI(三大现成UI控件)

Rawimage 可以是任意类型的图&#xff0c;所以这里的泛型就更宽泛&#xff0c;不止sprite 相比Image唯二的不同 uvrect有点像平铺 Text suddenly come to a Free island. best fit开启后会有范围选择 Image image 组件是挂在RectTransform的ui下的&#xff0c;换句话说&…

秋招内推2025-招联金融

【投递方式】 直接扫下方二维码&#xff0c;或点击内推官网https://wecruit.hotjob.cn/SU61025e262f9d247b98e0a2c2/mc/position/campus&#xff0c;使用内推码 igcefb 投递&#xff09; 【招聘岗位】 后台开发 前端开发 数据开发 数据运营 算法开发 技术运维 软件测试 产品策…

OBOO鸥柏丨户外液晶广告机有哪些功能可以定制室外广告牌?

OBOO鸥柏户外液晶广告机&#xff0c;犹如一位多才多艺的传播使者&#xff0c;巧妙融合信息传播、广告盛宴与互动乐园于一体&#xff0c;室外高亮数字标牌成为都市风景中一抹亮丽的智慧之光。鸥柏液晶户外广告牌轻盈穿梭于高速服务区的喧嚣、智慧城市的新能源充电站、收费站的繁…

Linux_kernel字符设备驱动12

一、字符设备的编程框架 在Linux_kernel驱动开发11中&#xff0c;我们介绍的系统调用。只是为了做一个实验&#xff0c;在真正开发时&#xff0c;我们并不会直接在内核中添加一个新的系统调用&#xff0c;这样做会导致内核体积变大。 1、字符设备结构体 我们实现一个硬件字符设…

WPF|依赖属性SetCurrentValue方法不会使绑定失效, SetValue方法会使绑定失效?是真的吗?

引言 最近因为一个触发器设置的结果总是不起效果的原因&#xff0c;进一步去了解[依赖属性的优先级](Dependency property value precedence - WPF .NET | Microsoft Learn)。在学习这个的过程中发现对SetCurrentValue一直以来的谬误。 在WPF中依赖属性Dependency property的…