2. JWT签发与基础验证实现

JWT(JSON Web )是目前分布式系统中最主流的签发格式。它将用户身份元数据编码为自包含的载荷,并通过数字签名确保完整性与真实性。本章将手把手实现HS256(对称)和RS256(非对称)两种签名算法的签发与验证,并编写可断言的单元测试,为后续自动化授权测试奠定基础。

2.1 原理概述:HS256与RS256

对比维度 HS256(对称) RS256(非对称)
密钥类型 单一密钥(Secret),签发和验证使用同一密钥 密钥对(Private/Public),签发用私钥,验证用公钥
性能 签名与验证速度极快(HMAC计算) 签名较慢(大数模幂运算),验证较快
安全模型 密钥必须保密,验证方需知晓密钥 私钥保密,公钥可以公开分发
适用场景 单一服务内部,或强信任网关环境 微服务架构、第三方开放API、跨组织信任链
常见算法标识 HS256 RS256
标准化选择:在自动化授权测试中,RS256是更推荐的选择,因为验证方只持有公钥,即使被攻破也无法签发新的,大大降低了横向攻击面。

2.2 核心Claims格式规范

JWT的负载(Payload)由一组“声明(Claims)”组成。遵循标准化的Claims字段可以确保跨平台的互操作性:

  • iss(Issuer):签发者,标识签发的服务方。
  • sub(Subject):主题,通常是用户的唯一标识(如user_id)。
  • aud(Audience):受众,接收的服务。
  • exp(Expiration Time):过期时间,使用UTC时间戳(秒级或毫秒级)。
  • nbf(Not Before):不早于时间,在此时间之前无效。
  • iat(Issued At):签发时间,用于刷新和时效性审计。
时区陷阱:exp和iat必须使用datetime.timezone.utc生成,禁止使用本地时间或未指定时区的datetime.now(),否则在跨时区验证时会直接抛出签名异常或过期异常。

2.3 签发实现流程

下图展示了JWT从签发到验证的完整生命周期,其中重点标出了签名算法和Claims校验的关键路径:

JWT签发与验证生命周期 用户 身份认证服务 JWT签发 JWT 客户端 存储 API服务器 验证签名 & Claims 签名有效? 授权成功 授权失败 注:HS256使用对称签名(单一密钥),RS256使用非对称签名(私钥签发 + 公钥验证)。

2.4 代码实战:使用PyJWT签发与验证

本节使用Python的PyJWT库演示核心实现。所有示例均使用UTC时间并包含完整的错误处理。

2.4.1 HS256实现

import jwt
import datetime

# 配置
SECRET_KEY = "my-super-secret-key-2024"
payload = {
    "sub": "user_12345",
    "name": "John Doe",
    "role": "editor",
    "iat": datetime.datetime.now(datetime.timezone.utc),
    "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=1)
}

# 签发
 = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
print(f"HS256 : {}")

# 验证
try:
    decoded = jwt.decode(, SECRET_KEY, algorithms=["HS256"])
    print(f"验证成功,用户ID: {decoded['sub']}")
except jwt.ExpiredSignatureError:
    print("已过期")
except jwt.InvalidSignatureError:
    print("签名无效")
except jwt.InvalidTokenError:
    print("格式错误")

2.4.2 RS256实现

import jwt
import datetime

# 从文件读取密钥对(实际生产环境应使用密钥管理服务)
with open("private.pem", "r") as f:
    PRIVATE_KEY = f.read()
with open("public.pem", "r") as f:
    PUBLIC_KEY = f.read()

payload = {
    "sub": "user_67890",
    "iat": datetime.datetime.now(datetime.timezone.utc),
    "exp": datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(hours=8)
}

# 使用私钥签发
 = jwt.encode(payload, PRIVATE_KEY, algorithm="RS256")
print(f"RS256 : {}")

# 使用公钥验证
try:
    decoded = jwt.decode(, PUBLIC_KEY, algorithms=["RS256"])
    print(f"RS256 验证通过,Subject: {decoded['sub']}")
except jwt.ExpiredSignatureError:
    print("已过期")
except jwt.PyJWTError as e:
    print(f"验证失败: {e}")

2.4.3 单元测试编写

自动化授权测试的核心在于可重复、可断言。以下是一个基于pytest的单元测试套件,覆盖了签名有效性、过期时间和负载完整性三个关键维度:

import jwt
import datetime
import pytest
SECRET = "test-secret-for-unit-test"
def test_hs256_signature_valid():
    """测试HS256签名验证通过"""
     = jwt.encode({"sub": "1"}, SECRET, algorithm="HS256")
    decoded = jwt.decode(, SECRET, algorithms=["HS256"])
    assert decoded["sub"] == "1"
def test_expired__raises_error():
    """测试过期的正确异常处理"""
    payload = {
        "sub": "1",
        "exp": datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(seconds=1)
    }
     = jwt.encode(payload, SECRET, algorithm="HS256")
    with pytest.raises(jwt.ExpiredSignatureError):
        jwt.decode(, SECRET, algorithms=["HS256"])
def test_invalid_signature_fails():
    """测试错误密钥导致的签名验证失败"""
     = jwt.encode({"sub": "1"}, "secret-key-a", algorithm="HS256")
    with pytest.raises(jwt.InvalidSignatureError):
        jwt.decode(, "secret-key-b", algorithms=["HS256"])
def test_iat_not_before():
    """测试iat(签发时间