You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中验证Azure访问令牌?签名验证失败排查

解决Azure AD访问令牌签名验证失败问题

以下是可能导致你用jwt.decode时出现“Signature verification failed”的原因及对应解决方案:

1. 公钥处理错误

你直接从discovery端点拿的x5c字段内容加证书头尾后作为字符串传入jwt.decode,这是错误的。PyJWT需要的是加密学意义上的公钥对象,而非字符串格式的证书。正确做法是把证书字符串解析成公钥对象:

  • 从JWK的x5c数组中取出第一个元素(Azure AD通常只返回一个),包裹证书头尾后,用cryptography库解析成X.509证书,再提取公钥。

2. 未匹配正确的密钥

每个JWT的header里包含kid(密钥ID),你需要从Azure AD返回的公钥集中找到kid完全匹配的密钥,不能随便拿一个公钥就用。如果密钥不匹配,签名肯定验证失败。

3. 令牌受众(aud)不匹配

确认你传入的audience参数和令牌中的aud声明完全一致:

  • 如果用户获取的是针对其他API的访问令牌,aud会是目标API的客户端ID,而非你的应用ID,这时候验证会失败。
  • 可以先用jwt.get_unverified_payload(token)查看令牌的aud值,确认是否和你的client_id一致。

4. 缺少发行者(issuer)验证

Azure AD的令牌发行者格式为https://login.microsoftonline.com/{tenant_id}/v2.0,验证时最好显式指定issuer参数,避免接受来自其他发行者的伪造令牌。

正确的验证代码示例

先安装依赖:

pip install pyjwt cryptography requests

验证代码:

import jwt
import requests
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

def get_azure_ad_public_keys(tenant_id):
    # 获取Azure AD公钥集
    url = f"https://login.microsoftonline.com/{tenant_id}/discovery/v2.0/keys"
    resp = requests.get(url)
    resp.raise_for_status()
    return resp.json()["keys"]

def get_matching_public_key(token, public_keys):
    # 根据令牌header的kid匹配对应公钥
    unverified_header = jwt.get_unverified_header(token)
    for key in public_keys:
        if key["kid"] == unverified_header["kid"]:
            # 解析证书为公钥对象
            cert_pem = f"-----BEGIN CERTIFICATE-----\n{key['x5c'][0]}\n-----END CERTIFICATE-----"
            cert = load_pem_x509_certificate(cert_pem.encode(), default_backend())
            return cert.public_key()
    raise ValueError("未找到与令牌匹配的公钥")

def verify_access_token(token, tenant_id, client_id):
    public_keys = get_azure_ad_public_keys(tenant_id)
    public_key = get_matching_public_key(token, public_keys)
    
    try:
        decoded_token = jwt.decode(
            token,
            public_key,
            algorithms=["RS256"],
            audience=client_id,
            issuer=f"https://login.microsoftonline.com/{tenant_id}/v2.0"
        )
        return decoded_token
    except jwt.InvalidTokenError as e:
        raise ValueError(f"令牌验证失败: {str(e)}")

# 使用示例
if __name__ == "__main__":
    TENANT_ID = "你的租户ID"
    CLIENT_ID = "你的客户端ID"
    USER_ACCESS_TOKEN = "用户传来的访问令牌"
    
    try:
        decoded = verify_access_token(USER_ACCESS_TOKEN, TENANT_ID, CLIENT_ID)
        print("验证成功,解码内容:", decoded)
    except ValueError as e:
        print("验证失败:", e)

额外排查步骤

  1. 先用jwt.get_unverified_header(token)和jwt.get_unverified_payload(token)查看令牌的元数据,确认:
    • alg字段是RS256
    • kid存在且能在公钥集中找到匹配项
    • aud等于你的client_id
    • iss符合Azure AD v2.0的格式
  2. 确认用户传入的是**访问令牌(access_token)**而非ID令牌(id_token),ID令牌的受众和用途和访问令牌不同。

内容的提问来源于stack exchange,提问作者khoshahmad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:13:26