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

使用PyJWT+Keycloak解码JWT时Audience必填问题求助

PyJWT在Keycloak环境下验证JWT的问题与解决

问题背景

我尝试用PyJWT官方文档里通过JWKS端点验证JWT的方法,在Keycloak环境下无法正常工作,后来通过搜索找到一篇博客,写出了可用代码,但遇到了audience相关的问题:

token_response = oauth.keycloak.authorize_access_token()
id_token = oauth.keycloak.parse_id_token(token_response)

# 从JWKS读取密钥并构建RSA公钥字典
jwk_uris = requests.get(f'{issuer}/.well-known/openid-configuration').json()["jwks_uri"]
jwks = requests.get(jwk_uris).json()

public_keys = {}
for jwk in jwks['keys']:
    kid = jwk['kid']
    public_keys[kid] = jwt.algorithms.RSAAlgorithm.from_jwk(json.dumps(jwk))

if id_token:
    logging.debug('logged in ' + id_token['email'])
    session['user'] = id_token
    token = token_response['access_token']
    # 获取token头部的密钥ID
    kid = jwt.get_unverified_header(token)['kid']
    # 获取对应的公钥
    key = public_keys[kid]
    try:
        session['resource_access'] = jwt.decode(token, key=key, audience="app_****", algorithms=["RS256"])['resource_access']
    except jwt.exceptions.MissingRequiredClaimError as exc:
        session['resource_access'] = {}

核心疑问

  1. 为什么调用jwt.decode时必须指定audience?
  2. 部分用户的token因无对应resource_access,不含aud字段,导致触发MissingRequiredClaimError,能否指定所有受众或忽略该字段?

解答

为什么需要指定audience?

aud(受众)是JWT的标准声明之一,作用是确保这个token是签发给特定接收方的,属于OAuth2/OIDC的安全校验机制。PyJWT默认会强制验证这个声明:如果调用jwt.decode时传入了audience参数,库就会检查token中的aud值是否与指定的受众匹配;如果token里根本没有aud字段,就会抛出MissingRequiredClaimError。

解决方法

方法1:直接忽略audience验证

可以在jwt.decode时添加options={"verify_aud": False}参数,跳过aud字段的校验,无论token里有没有这个字段都不会报错:

try:
    session['resource_access'] = jwt.decode(
        token,
        key=key,
        audience="app_****",
        algorithms=["RS256"],
        options={"verify_aud": False}
    ).get('resource_access', {})
except jwt.exceptions.MissingRequiredClaimError as exc:
    session['resource_access'] = {}

⚠️ 注意:这种方式会降低token的安全性,因为绕过了受众校验,仅适合内部完全信任的系统场景。

方法2:动态处理audience校验

如果不想完全关闭安全校验,可以先解析token的未验证payload,判断是否存在aud字段,再决定是否传入audience参数:

# 先获取未验证的payload(不校验签名,仅读取内容)
unverified_payload = jwt.decode(token, options={"verify_signature": False})

decode_params = {
    "key": key,
    "algorithms": ["RS256"]
}

# 如果token里有aud字段,再添加audience校验
if "aud" in unverified_payload:
    decode_params["audience"] = "app_****"
    # 若需要匹配多个受众,可传入列表:decode_params["audience"] = ["app_****", "other_client"]

try:
    decoded_token = jwt.decode(**decode_params)
    session['resource_access'] = decoded_token.get('resource_access', {})
except jwt.exceptions.MissingRequiredClaimError as exc:
    session['resource_access'] = {}

这种方式更安全,仅在token包含aud字段时才进行匹配校验,适配Keycloak中用户无对应客户端权限时token不含aud的场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 18:05:29