使用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'] = {}
核心疑问
- 为什么调用
jwt.decode时必须指定audience? - 部分用户的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
相关产品推荐
相关产品推荐

