Python操作 JWT(python-jose包)、哈希(passlib包)、用户验证完整流程

这篇具有很好参考价值的文章主要介绍了Python操作 JWT(python-jose包)、哈希(passlib包)、用户验证完整流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、JWT简介

JWT是什么?

  • JWT 即JSON 网络令牌(JSON Web Tokens)。

  • JWT(JSON Web Token) 是一种用于在身份提供者和服务提供者之间传递身份验证和授权数据的开放标准。JWT是一个JSON对象,其中包含了被签名的声明。这些声明可以是身份验证的声明、授权的声明等。JWT可以使用数字签名进行签名,以确保它不被篡改。

  • JWT 是一种将 JSON 对象编码为没有空格,且难以理解的长字符串的标准。JWT 的内容如下所示:

    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
    
  • JWT 字符串没有加密,任何人都能用它恢复原始信息。
    jwt官网及在线解码

  • 但 JWT 使用了签名机制。接受令牌时,可以用签名校验令牌。

  • 例如:使用 JWT 创建有效期为一周的令牌。第二天,用户持令牌再次访问时,仍为登录状态。令牌于一周后过期,届时,用户身份验证就会失败。只有再次登录,才能获得新的令牌。如果用户(或第三方)篡改令牌的过期时间,因为签名不匹配会导致身份验证失败。

JWT由三部分组成

JWT的格式为:xxx.xxx.xxx

  1. Header: 这部分包含了JWT的类型和签名所使用的算法。
  2. Payload: 也可以叫claims,这部分包含了JWT的声明,例如身份验证的声明和授权的声明。
  3. Signature: 这部分是使用算法和密钥对前两部分进行签名得到的。这部分用于验证JWT的发送者是否可信任。

python jwt,Python,python

Bearer JWT 和 JWT 不是同一个意思

  • JWT (JSON Web Token) 是一种用于在双方之间传递身份验证信息的标准。它是一个 JSON 对象,包含了被签名的声明。这些声明可以是身份验证的信息,比如用户名和密码,也可以是其他与此有关的信息,比如用户的权限。

  • Bearer JWT 是 JWT 的一种使用方式。在这种方式中,JWT 被用作认证授权的令牌,并通过 HTTP 请求的 Authorization 头部进行发送。这个头部的值是 Bearer空格 加上 JWT。

  • 所以 JWT 是一种身份验证和授权的标准, Bearer JWT 则是 JWT在HTTP请求中的一种传递方式。

进行JWT验证主要有以下几步

  1. 验证JWT签名:使用JWT的签名算法和密钥对JWT的签名部分进行验证,以确保JWT的发送者是可信任的。
  2. 验证JWT的有效期:使用JWT中的exp(expiration)和nbf(not before)声明来验证JWT是否在有效期内。
  3. 验证JWT的权限:使用JWT中的scope声明来验证用户是否有请求资源的权限。
  4. 验证JWT的其他声明:使用JWT中的其他声明来验证用户是否符合其他条件。

openssl rand -hex 32 什么意思?

openssl rand -hex 32是一个命令行命令,它使用 OpenSSL 库生成 32 个字符的十六进制随机字符串。

  • ‘openssl’ 是一种常用的工具,用于实现各种安全协议和算法,包括密钥生成、证书管理等。
  • ‘rand’ 是 OpenSSL 库的一个子命令,它用于生成随机数。
  • ‘-hex’ 指示 rand 子命令使用十六进制输出结果。
  • ‘32’ 是随机字符串的长度。

所以这个命令就是生成32位十六进制随机字符串。这种随机字符串常用来做盐值,可以用来加密密码,来防止密码被破解。

例如:

$ openssl rand -hex 32
> 6b48014c061a82bb3c6fd4812777552cf41a79c38149e31516b818a73d50ee51
  • 这里主要用于JWT的Signature,以确保JWT的发送者是可信任的。即创建用于 JWT 令牌签名的随机密钥。

安装 python-jose

  • python-jose包用于在Python中 生成和校验 JWT 令牌

  • python-jose官网

  • python-jose需要安装配套的加密后端,例如下面我们使用的后端是pyca/cryptography

$ pip3 install "python-jose[cryptography]"
  • 除了python-jose,相似的还有PyJWT,但还是推荐Python-jose ,因为支持 PyJWT 的所有功能,还支持与其它工具集成时可能会用到的一些其它功能。

二、密码哈希

哈希是指把特定内容(本例中为密码)转换为乱码形式的字节序列(其实就是字符串)。

每次传入完全相同的内容时(比如,完全相同的密码),返回的都是完全相同的乱码。

但这个乱码无法转换回传入的密码。

为什么使用密码哈希

原因很简单,假如数据库被盗,窃贼无法获取用户的明文密码,得到的只是哈希值。

这样一来,窃贼就无法在其它应用中使用窃取的密码,要知道,很多用户在所有系统中都使用相同的密码,风险超大)。

bcrypt算法是什么?

  • bcrypt是一种密码哈希算法。它通过对用户的密码进行加密来保护它们。

  • bcrypt使用一种称为"慢哈希"的技术来提高安全性。在这种技术中,算法会在计算哈希值时进行大量迭代,从而使得暴力破解变得更加困难。

  • bcrypt 也支持一种称为 “盐” 的概念。盐是一串随机字符串,它被附加到密码上,然后一起计算哈希值。这样即使两个用户使用了相同的密码,它们的哈希值也会不同。

  • bcrypt 是被广泛接受的密码哈希算法,因为它能够高效地防止暴力破解,并且它的实现方式可以防止在多种并行计算环境中的高速破解。

  • 在Python中可以使用bcrypt库来使用bcrypt算法。

    import bcrypt
    
    password = b"supersecretpassword"
    # Hash a password for the first time, with a randomly-generated salt
    hashed = bcrypt.hashpw(password, bcrypt.gensalt())
    
    # Check that an unencrypted password matches one that has
    # previously been hashed
    if bcrypt.checkpw(password, hashed):
        print("It Matches!")
    else:
        print("It Does not Match :(")
    

安装 passlib

  • Passlib 是处理密码哈希的 Python 包。

  • 它支持很多安全哈希算法及配套工具。

  • 本教程推荐的算法是 Bcrypt。因此,请先安装附带 Bcrypt 的 PassLib:

    $ pip3 install passlib[bcrypt]
    
  • passlib 甚至可以读取 Django、Flask 的安全插件等工具创建的密码。

    例如,把 Django 应用的数据共享给 FastAPI 应用的数据库。或利用同一个数据库,可以逐步把应用从 Django 迁移到 FastAPI。

    并且,用户可以同时从 Django 应用或 FastAPI 应用登录。

"""
passlib.context.CryptContext 类可以用来配置和管理密码哈希算法。
这个实例的意思是:使用 "bcrypt" 算法对密码进行哈希,并使用 "auto" 模式对过时的算法进行处理。
"bcrypt" 是一种常用的密码哈希算法,它可以防止密码被暴力破解。
"auto" 模式表示自动处理过时的算法,例如在验证时优先使用最新的算法,如果失败则尝试使用旧的算法。
这个配置可以确保密码安全性,并且对过时算法进行兼容性处理。
Passlib 是一个强大的密码管理库,提供了一组密码哈希,验证和生成密码的工具,并且支持多种常用的密码哈希算法。
"""

from passlib.context import CryptContext

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

def verify_password(plain_password, hashed_password):
    """验证输入的密码的hash密码与数据库中记录的hash密码是否是一样的
    :param plain_password: 用户输入的密码
    :param hashed_password: hash密码
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
    """把密码转为hash密码
    """
    return pwd_context.hash(password)

if __name__ == '__main__':
    password_hash = get_password_hash("Abc123.")
    print(password_hash)  # 注意:每次运行打印的值都是不同的

    print(verify_password("Abc123.", password_hash))  # True
    print(verify_password("abc123.", password_hash))  # False

三、Python实现用户验证的完整流程(用于生产环境)

from jose import JWTError, jwt
from datetime import datetime, timedelta
from typing import Union
from passlib.context import CryptContext
from pydantic import BaseModel

# 要获取如下所示的字符串,请运行:openssl rand -hex 32
# SECRET_KEY用于JWT令牌签名的随机密钥
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
# 指定 JWT 令牌签名算法的变量 ALGORITHM
ALGORITHM = "HS256"
# 设置令牌过期时间的变量,这里是30分钟
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 假数据,假如这是数据库中的用户表的全量数据
fake_users_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        # 下面的hash password对应的明文密码为Abc123.
        "hashed_password": "$2b$12$oC25Sks9Kg8WU8N3ddoeNugEYYQDQU9Cph7aLvP9VL.uNykdgCQQG",
        "disabled": False,
    }
}

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


class User(BaseModel):
    username: str
    email: Union[str, None] = None
    full_name: Union[str, None] = None
    disabled: Union[bool, None] = None


class UserInDB(User):
    hashed_password: str


class TokenData(BaseModel):
    username: Union[str, None] = None


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """验证输入的密码的hash密码与数据库中记录的hash密码是否是一样的
    :param plain_password: 用户输入的密码
    :param hashed_password: hash密码
    :return:
    """
    return pwd_context.verify(plain_password, hashed_password)


def get_user(db: dict, username: str) -> UserInDB:
    """模拟在数据库中查找用户,找到之后初始化UserInDB类并返回实例"""
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)


def authenticate_user(fake_db: dict, username: str, password: str) -> Union[bool, UserInDB]:
    """
    先验证$username用户是否在数据库中存在,存在则继续验证用户输入的明文密码与数据库中记录的hash密码是否匹配
    如果都没问题就返回<class '__main__.UserInDB'>
    :param fake_db:
    :param username:
    :param password:
    :return:
    """
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):  # user是UserInDB类的实例,所以可以点属性
        return False
    return user


def create_access_token(data: dict, expires_delta: Union[timedelta, None] = None) -> str:
    """创建带exp字段的JWT字符串"""
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.utcnow() + expires_delta  # 这里是utc时间,不是东八区时间
        # print(expire)  # 2023-01-18 08:14:02.453944
    else:
        expire = datetime.utcnow() + timedelta(minutes=15)
    to_encode.update({"exp": expire})  # datetime.datetime(2023, 1, 18, 8, 14, 02, 453944)

    # SECRET_KEY对声明集进行签名的密钥
    # jwt.encode()对声明集进行编码并返回 JWT 字符串。
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def get_current_user(token: str) -> Union[UserInDB, None]:
    """
    解密JWT,即验证JWT字符串的SIGNATURE签名并返回claims(也称PAYLOAD)的信息
    :param token:
    :return:
    """
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        # print(payload)  # {'sub': 'johndoe', 'exp': 1674033230}
        username: str = payload.get("sub")
        if username is None:
            print("username 不存在")
        token_data = TokenData(username=username)
        user = get_user(fake_users_db, username=token_data.username)
        return user
    except JWTError as e:
        print(e)  # Signature verification failed.


if __name__ == '__main__':
    # form_data是模拟Request body的参数
    form_data = {
        "username": "johndoe",
        "password": "Abc123."
    }

    # 创建token过期时间
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    # print(access_token_expires)  # <class 'datetime.timedelta'> 0:30:00

    # 验证Request body传入的用户是否在数据库中存在
    # 如果存在则比对password与数据库中记录的hash password是否匹配
    # 最终,没问题则返回该用户的UserInDB类的实例对象user;有问题则返回False
    user = authenticate_user(fake_users_db, form_data.get("username"), form_data.get("password"))
    print(user)

    # 创建PAYLOAD带exp字段、sub字段的JWT字符串
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    print(access_token)  # 解码用https://jwt.io/

    # 根据JWT获取当前用户
    current_user = get_current_user(access_token)
    print(current_user)

当然,我们还可以为token添加权限。

划重点,sub 键在整个应用中应该只有一个唯一的标识符,而且应该是字符串。文章来源地址https://www.toymoban.com/news/detail-789095.html

到了这里,关于Python操作 JWT(python-jose包)、哈希(passlib包)、用户验证完整流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • pyjwt,一个强大的 Python JWT解析校验库!

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 目录 ​编辑 前言 什么是PyJWT? 安装PyJWT PyJWT的基本概念 创建JWT 解析JWT 验证JWT 高级功能和实际应用场景  1. 自定义过期时间处理  2. 加密

    2024年02月20日
    浏览(26)
  • Python中的哈希表

    哈希表是一种常用的数据结构,广泛应用于字典、散列表等场合。它能够在O(1)时间内进行查找、插入和删除操作,因此被广泛应用于各种算法和软件系统中。 哈希表的实现基于哈希函数,将给定的输入映射到一个固定大小的表格中,每个表项存储一个/值对。哈希函数

    2024年02月14日
    浏览(28)
  • python3 Flask jwt 简易token认证实例

        chatgpt写的代码

    2024年02月22日
    浏览(45)
  • Python数据结构:哈希表

    散列(哈希)是电脑科学中一种对资料的处理方法,通过某种特定的函数/算法(称为散列函数/算法)将要检索的项与用来检索的索引(称为散列,或者散列值)关联起来,生成一种便于搜索的数据结构(称为散列表)。 哈希表是什么 哈希表(散列表)是根据键(Key)直接访

    2024年02月12日
    浏览(35)
  • Hash(哈希)算法-Python实现

    哈希算法将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值称为哈希值。哈希值是一段数据唯一且极其紧凑的数值表示形式。如果散列一段明文而且哪怕只更改该段落的一个字母,随后的哈希都将产生不同的值。要找到散列为同一个值的两个不同的

    2023年04月15日
    浏览(34)
  • Python 哈希表的实现——字典

    哈喽大家好,我是咸鱼 接触过 Python 的小伙伴应该对【字典】这一数据类型都了解吧 虽然 Python 没有显式名称为“哈希表”的内置数据结构,但是字典是哈希表实现的数据结构 在 Python 中,字典的键(key)被哈希,哈希值决定了键对应的值(value)在字典底层数据存储中的位

    2024年02月05日
    浏览(40)
  • 初学python记录:力扣705. 设计哈希集合

    不使用任何内建的哈希表库设计一个哈希集合(HashSet)。 实现  MyHashSet  类: void add(key)  向哈希集合中插入值  key  。 bool contains(key)  返回哈希集合中是否存在这个值  key  。 void remove(key)  将给定值  key  从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

    2024年04月15日
    浏览(25)
  • Python小知识 - 一致性哈希算法

    一致性哈希算法 一致性哈希算法(Consistent Hashing Algorithm)是用于解决分布式系统中节点增减比较频繁的问题。它的思想是,将数据映射到0~2^64-1的哈希空间中,并通过哈希函数对数据进行映射,计算出数据所在的节点。当节点增加或减少时,只需要重新计算数据所在的节点即

    2024年02月09日
    浏览(48)
  • 【Python查找算法】二分查找、线性查找、哈希查找

    目录 1 二分查找算法  2 线性查找算法 3 哈希查找算法         二分查找(Binary Search)是一种用于在有序数据集合中查找特定元素的高效算法。它的工作原理基于将数据集合分成两半,然后逐步缩小搜索范围,直到找到目标元素或确定目标元素不存在。 以下是二分查找的

    2024年02月08日
    浏览(51)
  • 【Python开发手册】JWT Token中添加过期时间和角色:简单易学的pyjwt

    💖 作者简介:大家好,我是Zeeland,全栈领域优质创作者。 📝 CSDN主页:Zeeland🔥 📣 我的博客:Zeeland 📚 Github主页: Undertone0809 (Zeeland) (github.com) 🎉 支持我:点赞👍+收藏⭐️+留言📝 📣 系列专栏:Python系列专栏 🍁 💬介绍:The mixture of software dev+Iot+ml+anything🔥 【cushy-s

    2023年04月23日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包