python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > PyJWT Token验证

PyJWT实现Token验证

作者:T0uken

Python 的 PyJWT 是一个流行的库,用于处理 JWT,本文主要介绍了PyJWT实现Token验证,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

JSON Web Token (JWT) 是一种基于 JSON 格式的轻量级的安全令牌,通常用于身份验证和信息交换。Python 的 PyJWT 是一个流行的库,用于处理 JWT。本文将从零开始,带你逐步学习 PyJWT 的基本用法、原理以及进阶功能。

什么是 JWT

JWT 的组成

JWT 是由三部分组成的字符串:

它们通过 . 分隔,整体形式为:

Header.Payload.Signature

例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxMjMsIm5hbWUiOiJKb2UiLCJyb2xlIjoiYWRtaW4ifQ.4zUHL8hfXX8E8OomNQByT3MHnfoyQh27-8r1ZjXv9Fo

JWT 的工作流程

安装 PyJWT

在开始使用 PyJWT 之前,先安装它:

pip install PyJWT

生成 JWT

创建简单的 JWT

下面是如何使用 PyJWT 生成一个简单的 JWT:

import jwt
import datetime

# 定义密钥和算法
SECRET_KEY = "your_secret_key"
ALGORITHM = "HS256"

# 生成 JWT
payload = {
    "user_id": 123,
    "name": "John Doe",
    "role": "admin",
    "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1)  # 设置过期时间
}

token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
print("Generated JWT:", token)

代码说明:

解码 JWT

要解码生成的 JWT 并查看数据:

decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
print("Decoded Payload:", decoded_payload)

检查过期时间

如果令牌过期,会抛出 jwt.ExpiredSignatureError 异常:

try:
    decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
    print("Decoded Payload:", decoded_payload)
except jwt.ExpiredSignatureError:
    print("Token has expired!")

JWT 的签名和验证

PyJWT 支持多种算法,如:

对称加密

对称加密使用单一密钥签名和验证:

# 对称加密签名
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")

# 验证签名
decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])

非对称加密

非对称加密使用公钥和私钥。示例:

高级功能

自定义声明

除了标准声明(如 expiat),你可以添加自定义字段:

payload = {
    "user_id": 123,
    "role": "editor",
    "permissions": ["read", "write"],
    "iat": datetime.datetime.utcnow(),  # 签发时间
    "exp": datetime.datetime.utcnow() + datetime.timedelta(days=1)  # 过期时间
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")

设置过期时间

exp 是 JWT 的标准声明,用于指定过期时间。

payload = {
    "user_id": 123,
    "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
}

令牌刷新

令牌刷新可以延长用户登录会话时间:

# 原令牌
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")

# 解码并刷新
decoded = jwt.decode(token, SECRET_KEY, algorithms=["HS256"], options={"verify_exp": False})
decoded["exp"] = datetime.datetime.utcnow() + datetime.timedelta(minutes=15)

# 生成新令牌
refreshed_token = jwt.encode(decoded, SECRET_KEY, algorithm="HS256")

常见错误及解决方法

Token 已过期

错误信息:jwt.ExpiredSignatureError

解决方法:在 jwt.decode 时捕获异常。

try:
    decoded_payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
except jwt.ExpiredSignatureError:
    print("Token has expired!")

签名验证失败

错误信息:jwt.InvalidSignatureError

解决方法:确保签名密钥正确,并使用相同的算法。

实战:JWT 身份验证示例

以下是一个基于 Fastapi的示例,实现用户登录和令牌验证:

from fastapi import FastAPI, Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
import jwt
import datetime
from typing import Dict, Optional

# ==========================
# 配置和常量
# ==========================
SECRET_KEY = "your_secret_key"  # JWT 密钥
ALGORITHM = "HS256"            # JWT 算法
TOKEN_EXPIRE_HOURS = 1         # 令牌有效时间(小时)

# OAuth2PasswordBearer 定义,用于获取用户的令牌
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


# ==========================
# 用户服务类
# ==========================
class UserService:
    """
    用户服务类:负责用户身份验证和管理
    """
    def __init__(self):
        # 模拟数据库:用户名和密码
        self.users_db = {
            "admin": "password123",
            "user": "mypassword"
        }

    def authenticate(self, username: str, password: str) -> bool:
        """
        验证用户名和密码是否匹配
        :param username: 用户名
        :param password: 密码
        :return: 验证是否成功
        """
        return self.users_db.get(username) == password


# ==========================
# JWT 工具类
# ==========================
class JWTHandler:
    """
    JWT 工具类:负责生成和验证 JWT
    """
    @staticmethod
    def create_token(username: str, expire_hours: int = TOKEN_EXPIRE_HOURS) -> str:
        """
        生成 JWT
        :param username: 用户名
        :param expire_hours: 令牌有效时间
        :return: 生成的 JWT 字符串
        """
        payload = {
            "sub": username,  # 声明主体
            "exp": datetime.datetime.utcnow() + datetime.timedelta(hours=expire_hours)  # 过期时间
        }
        return jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)

    @staticmethod
    def verify_token(token: str) -> Optional[Dict]:
        """
        验证 JWT 并解码
        :param token: JWT 字符串
        :return: 解码后的 Payload,如果无效则返回 None
        """
        try:
            return jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        except jwt.ExpiredSignatureError:
            raise HTTPException(status_code=401, detail="令牌已过期")
        except jwt.InvalidTokenError:
            raise HTTPException(status_code=401, detail="令牌无效")


# ==========================
# 依赖注入类
# ==========================
class Dependencies:
    """
    依赖注入类,用于解耦依赖
    """
    def __init__(self, user_service: UserService, jwt_handler: JWTHandler):
        self.user_service = user_service
        self.jwt_handler = jwt_handler

    def authenticate_user(self, form_data: OAuth2PasswordRequestForm) -> str:
        """
        验证用户并生成 JWT
        :param form_data: 用户提交的表单数据(包含用户名和密码)
        :return: JWT
        """
        username = form_data.username
        password = form_data.password
        if not self.user_service.authenticate(username, password):
            raise HTTPException(status_code=401, detail="用户名或密码错误")
        return self.jwt_handler.create_token(username)

    def validate_token(self, token: str) -> str:
        """
        验证 Token 并返回用户名
        :param token: JWT
        :return: 解码后的用户名
        """
        payload = self.jwt_handler.verify_token(token)
        username = payload.get("sub")
        if not username:
            raise HTTPException(status_code=401, detail="无效的令牌")
        return username


# ==========================
# 创建 FastAPI 应用
# ==========================
app = FastAPI()

# 实例化服务类
user_service = UserService()
jwt_handler = JWTHandler()
dependencies = Dependencies(user_service, jwt_handler)


# ==========================
# 路由定义
# ==========================
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
    """
    登录接口:验证用户并返回 Token
    :param form_data: FastAPI 提供的 OAuth2PasswordRequestForm
    :return: 包含 Token 的 JSON
    """
    token = dependencies.authenticate_user(form_data)
    return {"access_token": token, "token_type": "bearer"}


@app.get("/protected")
async def protected(token: str = Depends(oauth2_scheme)):
    """
    受保护的接口:需要提供有效 Token 才能访问
    :param token: OAuth2PasswordBearer 自动提取的 JWT
    :return: 欢迎信息
    """
    username = dependencies.validate_token(token)
    return {"message": f"欢迎回来,{username}!您的访问已被授权。"}


# ==========================
# 主程序入口(仅调试使用)
# ==========================
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="127.0.0.1", port=8000)

代码说明

测试流程

总结

在这篇文章中,我们深入探讨了 JSON Web Token(JWT)的基本原理,尤其是如何使用 PyJWT 库来生成、解码和验证令牌。我们首先了解了 JWT 的工作机制,并学习了签名算法及其进阶功能,以确保令牌的安全性和可靠性。此外,我们还讨论了一些常见问题及其解决方法,以便帮助你更好地应对这些在实际应用中可能遇到的挑战。最后,我们通过一个完整的 JWT 身份验证实例,将理论与实践相结合,希望能为你的项目提供有价值的参考。掌握这些内容后,你将能够灵活地在自己的 Web 应用中应用 JWT,提升系统的安全性和用户体验。

到此这篇关于PyJWT实现Token验证的文章就介绍到这了,更多相关PyJWT Token验证内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文