Azure APIM自定义政策实现JWKs缓存用于JWT校验的问题咨询
问题1解答
AsJwt()方法返回的JWT对象默认不暴露头部字段,需要手动拆分JWT的头部段解码读取:
JWT由.分割为三部分,第一部分为base64url编码的头部,将其转换为标准base64解码后即可读取kid、x5t等字段,参考写法:
<set-variable name="requestKid" value="@( context.Request.Headers.GetValueOrDefault("Authorization","") .Split(' ').LastOrDefault()?.Split('.').FirstOrDefault() is string headerRaw ? Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject>( System.Text.Encoding.UTF8.GetString( Convert.FromBase64String(headerRaw.Replace('_', '/').Replace('-', '+') .PadRight(headerRaw.Length + (4 - headerRaw.Length % 4) % 4, '=')) ) )["kid"].ToString() : null )" />
问题2解答
上下文变量不会被销毁,问题出在响应体的读取机制:IResponse.Body默认是向前只读的流,第一次读取后流指针会走到末尾,第二次读取默认就会返回空。
解决方法是首次读取响应体时传入preserveContent: true参数,读完后自动重置流指针,也可以直接把完整的JWKS对象提前存入独立变量复用:
<send-request mode="new" response-variable-name="jwksResponse" timeout="10" ignore-error="true"> <set-url>https://some.identity.server/.well-known/openid-configuration/jwks</set-url> <set-method>GET</set-method> </send-request> <!-- 先把完整JWKS存入变量,后续都从这个变量读取 --> <set-variable name="jwksDoc" value="@(((IResponse)context.Variables["jwksResponse"]).Body.As<Newtonsoft.Json.Linq.JObject>(preserveContent: true))" /> <set-variable name="jwksn" value="@((string)context.Variables["jwksDoc"]["keys"][0]["n"])" /> <set-variable name="jwkse" value="@((string)context.Variables["jwksDoc"]["keys"][0]["e"])" />
问题3解答
这个错误是APIM设计时的XML静态校验导致的:validate-jwt政策的e、n属性在schema中被定义为base64url格式,动态表达式在设计阶段无法被解析为具体值,因此会被判定为非法格式,没有办法直接绕过这个属性校验。
推荐改用x5c方式传入公钥,或者将模数和指数拼接为标准公钥格式后作为<key>节点的内容传入,节点内部的动态表达式不会触发该静态校验。
问题4解答
可以直接使用x5c值作为密钥,你的参考写法完全符合要求:x5c数组的第一个元素就是证书DER编码的标准base64字符串,APIM的validate-jwt政策原生支持直接读取该格式的公钥,同时这种写法也能绕开问题3提到的静态校验错误,是目前实现动态JWKS验证的最优方案。
缓存优化提示
你可以搭配<cache-store-value>和<cache-lookup-value>政策把JWKS结果缓存到APIM内置缓存,过期时间可设置为12~24小时,进一步减少对身份提供商端点的请求频次。
内容的提问来源于stack exchange,提问作者Dominik Krišš

