python verify jwt

admin 55 0
在Python中验证JWT(JSON Web Token)通常使用PyJWT库,核心步骤包括:加载待验证token,通过预设密钥(对称加密)或公钥(非对称加密)验证签名合法性;检查声明(如exp过期时间、iss签发者等)是否符合预期;捕获PyJWT异常(如ExpiredSignatureError、InvalidTokenError)处理无效或过期token,需注意密钥安全,避免泄露,严格遵循JWT规范(RFC 7519),保障身份认证与数据传输安全。

Python 中如何验证 JWT:从原理到实践

在现代化的 Web 应用和 API 开发中,JWT(JSON Web Token)已成为用户认证和信息传递的主流方案,它以紧凑的形式(如 header.payload.signature)安全地传输用户身份、权限等数据,避免了传统 Session 机制的服务端存储压力,JWT 的核心优势——基于签名的安全性,也依赖于严格的验证流程,如果验证不当,攻击者可能篡改 JWT 内容(如修改用户权限、延长过期时间),导致安全漏洞。

本文将详细介绍 JWT 的验证原理,并重点讲解如何在 Python 中使用 PyJWT 库实现 JWT 验证,涵盖签名验证、声明校验、错误处理等关键环节。

JWT 基础:结构解析

JWT 由三部分组成,通过 连接:

头部(Header)

声明令牌的类型(typ: "JWT")和签名算法(如 alg: "HS256"alg: "RS256"),通常为 Base64 编码的 JSON 对象。
示例:

{
  "alg": "HS256",
  "typ": "JWT"
}

载荷(Payload)

包含声明(Claims),即用户信息和元数据(如用户 ID、过期时间 exp、签发者 iss 等),声明分为:

  • 注册声明(Registered Claims):如 iss(签发者)、exp(过期时间)、sub(主题)、aud(受众)等,推荐使用但非必须。
  • 公共声明(Public Claims):自定义声明,需避免冲突。
  • 私有声明(Private Claims):用户自定义的声明,用于业务逻辑。
    示例:
    {
    "sub": "user123",
    "name": "Alice",
    "exp": 1735689600,  // 2025-01-01 00:00:00 UTC
    "iss": "https://myapp.com"
    }

签名(Signature)

通过以下公式生成:

signature = HMAC-SHA256(base64url(header) + "." + base64url(payload), secret_key)

签名的作用是验证 JWT 的完整性和真实性:确保头部和载荷在传输过程中未被篡改,且由可信方签发。

为什么必须验证 JWT?

JWT 的安全性完全依赖于签名验证,如果仅解析 JWT 而不验证签名,攻击者可以:

  • 篡改载荷中的用户权限(如将普通用户改为管理员);
  • 修改过期时间 exp,使过期令牌“复活”;
  • 伪造签发者 iss,伪装为可信来源。

验证签名是 JWT 安全的第一道防线,必须严格校验。

Python 中验证 JWT:使用 PyJWT 库

PyJWT 是 Python 中处理 JWT 的标准库,支持多种签名算法(如 HS256、RS256)和声明校验。

安装 PyJWT

pip install PyJWT

核心验证流程

验证 JWT 的核心步骤包括:

  1. 解析 JWT(分离头部、载荷、签名);
  2. 验证签名(确保头部和载荷未被篡改);
  3. 校验声明(如过期时间、签发者等)。
示例 1:对称加密(HS256)验证

HS256 使用共享密钥(secret key)进行签名和验证,适用于服务端与客户端共享密钥的场景(如内部 API 调用)。

import jwt
from jwt import ExpiredSignatureError, InvalidSignatureError, DecodeError
# 假设这是从客户端收到的 JWT
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIiwibmFtZSI6IkFsaWNlIiwiZXhwIjoxNzM1Njg5NjAwLCJpc3MiOiJodHRwczovL215YXBwLmNvbSJ9.signature"
# 共享密钥(必须与签发时使用的密钥一致)
secret_key = "my_super_secret_key"
try:
    # 解析并验证 JWT
    payload = jwt.decode(
        jwt_token,
        secret_key,
        algorithms=["HS256"],  # 指定签名算法,必须与 JWT 头部一致
        options={
            "verify_signature": True,  # 默认开启,显式指定更清晰
            "verify_exp": True,       # 验证过期时间
            "verify_iss": True,       # 验证签发者
            "iss": "https://myapp.com" # 指定允许的签发者
        }
    )
    print("JWT 验证成功!载荷内容:", payload)
except ExpiredSignatureError:
    print("错误:JWT 已过期")
except InvalidSignatureError:
    print("错误:JWT 签名无效(可能被篡改或密钥错误)")
except DecodeError:
    print("错误:JWT 格式无效")
except jwt.InvalidIssuerError:
    print("错误:JWT 签发者不符合预期")
except Exception as e:
    print("未知错误:", e)
示例 2:非对称加密(RS256)验证

RS256 使用私钥签名、公钥验证,适用于跨服务通信或开放 API 场景(如第三方登录)。

签发 JWT(使用私钥)

import jwt
# 假设这是从文件中读取的私钥(实际应从安全存储加载)
private_key = """-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC7...(省略私钥内容)
-----END PRIVATE KEY-----"""
# 生成 JWT
payload = {
    "sub": "user123",
    "name": "Alice",
    "exp": 1735689600,
    "iss": "https://myapp.com"
}
jwt_token = jwt.encode(payload, private_key, algorithm="RS256")
print("签发的 JWT:", jwt_token)

验证 JWT(使用公钥)

# 假设这是从文件中读取的公钥(可公开分享)
public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7...(省略公钥内容)
-----END PUBLIC KEY-----"""
try:
    payload = jwt.decode(
        jwt_token,
        public_key,
        algorithms=["RS256"],
        options={
            "verify_exp": True,
            "verify_iss": True,
            "iss": "https://myapp.com"
        }
    )
    print("JWT 验证成功!载荷内容:", payload)
except Exception as e:
    print("验证失败:", e)

关键声明校验

PyJWToptions 参数允许灵活控制声明校验规则,常用声明包括:

  • exp(过期时间):必须校验,防止使用过期令牌;
  • iss(签发者):确保令牌来自可信服务;
  • aud(受众):确保令牌是发给当前应用(如 aud="my_api");
  • nbf(生效时间):令牌在指定时间前无效(如 nbf=1735686000);
  • iat(签发时间):记录令牌签发时间,可用于审计。

示例:自定义声明校验

payload = jwt.decode(
    jwt_token,
    secret_key,
    algorithms=["HS256"],
    options={
        "verify_exp": True,
        "verify_iss": True,
        "iss": "https://myapp.com",
        "verify_aud": True,
        "aud": "my_api"  # 仅允许受众为 "my_api" 的令牌
    }
)

错误处理

JWT 验证可能抛出多种异常,需针对性处理:
| 异常类型 | 说明 |
|----------|------|
| ExpiredSignatureError | JWT 已过期(exp 早于当前时间) |
| InvalidSignatureError | 签名无效(密钥错误或内容被篡改) |
| DecodeError | JWT 格式错误(如 Base64 编码问题) |
| InvalidIssuerError | 签发者 iss 不符合预期 |
| InvalidAudienceError | 受众 aud 不符合预期 |
| ImmatureSignatureError | 生效时间 nbf 未到 |

安全注意事项

  1. 密钥管理

    • 对称密钥(HS256)必须保密,避免硬编码在代码中,建议通过环境变量或密钥管理服务(如 AWS KMS)加载;
    • 非对称密钥(RS256)的私钥需严格保护,公钥可公开。
  2. 算法安全

    • 禁止使用不安全的算法(如 none,可绕过签名验证);
    • 确保算法与 JWT 头部一致,避免算法混淆攻击(如头部声明 alg: "HS256",但实际使用公钥验证)。
  3. 敏感信息

    • JWT 载荷是 Base64 编码(非加密),不要存储密码等敏感数据;
    • 对敏感操作(如修改密码),即使 JWT 有效,也应二次校验(如密码验证)。
  4. 令牌有效期

    • 设置合理的过期时间(如 exp=1小时),避免长期有效的令牌;
    • 对于敏感场景,可结合刷新令牌(Refresh Token)实现短期令牌自动更新。

在 Python 中验证 JWT,核心是使用 PyJWT 库完成签名验证声明校验

  1. 通过 jwt.decode() 方法,传入密钥/公钥和算法列表;
  2. 通过 options 参数控制声明校验规则(如 expiss);
  3. 捕获常见异常,提供友好的错误提示。

JWT 的安全性依赖于“验证签名+严格声明校验”的双重保障,开发者需牢记:未经验证的 JWT 不可信,结合密钥管理和安全实践,才能充分发挥 JWT 在认证和授权中的优势。

标签: #python jwt