适用版本: 7.x-8.x
1. 错误异常的基本描述 #
Verify failed because all ... provided JWKs were filtered. 表示 Elasticsearch 在 JWT/JWS 签名验证流程中,虽然成功获取到了 JWK(JSON Web Key)集合,但在按照 kid、算法、用途、密钥长度或曲线等条件进行过滤后,没有任何一把密钥能够满足校验条件,导致验签流程无法继续。
这个异常的本质是:不是签名验证失败,而是在进入真正验签之前,候选密钥就已经被全部过滤掉了。
常见现象 #
- JWT、ID Token 或 JWS 校验失败,接口返回
401 Unauthorized或403 Forbidden。 - Elasticsearch 日志中出现类似
Verify failed because all N provided JWKs were filtered.的错误信息。 - 使用 OpenID Connect、SAML 或自定义 JWT 认证时,用户无法正常登录。
- 常见于密钥轮换、算法切换、安全策略升级或 OIDC Provider 配置变更之后。
- 如果应用在重试,可能会看到大量相同错误,直到 JWK 配置被修正或缓存刷新。
典型报错与异常栈 #
常见日志形态通常类似下面这样:
ElasticsearchSecurityException: Verify failed because all 3 provided JWKs were filtered.
Caused by: java.lang.IllegalArgumentException
at org.elasticsearch.xpack.security.authc.jwt.JwtSignatureValidator.validate(JwtSignatureValidator.java:XX)
at org.elasticsearch.xpack.security.authc.jwt.JwtRealmAuthenticator.authenticate(JwtRealmAuthenticator.java:XX)
2. 为什么会发生这个错误 #
从 Elasticsearch 源码逻辑来看,JWT 签名验证流程大致是:获取 JWK Set → 按过滤条件筛选 → 用筛选后的密钥验签。该异常发生在第二步:筛选条件过于严格,导致没有密钥留下来。
常见原因通常包括:
kid不匹配:Token Header 中的kid(Key ID)与 JWK Set 中任何密钥的kid都不一致。这是最常见的原因,尤其是密钥轮换后新密钥使用了新的kid,但 OIDC Provider 的 JWKS 端点还没更新或缓存未刷新。- 算法不匹配:Token Header 中的
alg与 JWK 的alg或推导出的算法不一致。例如 Token 用RS256签名,但 JWK 声明的是PS256或未声明算法。 - 密钥类型不匹配:JWK 的
kty(Key Type)与签名算法要求的密钥类型不同。例如alg: ES256需要kty: EC,但 JWK 提供的是kty: RSA。 - 密钥用途不匹配:JWK 的
use(Usage)字段不是sig(签名),或者是key_ops不包含verify,导致被过滤掉。 - 安全强度限制过严:Elasticsearch 配置了最低密钥强度要求(如 RSA 密钥长度至少 2048 位),而 JWK Set 中的密钥强度不达标。
- 曲线不匹配:对于椭圆曲线密钥(EC),JWK 的
crv(Curve)与算法要求的曲线不一致,例如ES256要求P-256曲线。 - JWK Set 配置错误:JWK Set 本身配置有误,例如
keys数组为空、JSON 格式错误、或者 JWK 字段缺失关键信息。
3. 如何排查和解决这个异常 #
建议按"先定位 Token 与 JWK 的不匹配点,再修正配置"的顺序处理:
解码 JWT Token Header:使用
jwt.io或命令行工具解码 Token 的 Header 部分,记录kid、alg字段的值。echo "eyJhbGciOiJSUzI1NiIsImtpZCI6ImFiYzEyMyJ9..." | cut -d'.' -f1 | base64 -d | jq .输出示例:
{ "alg": "RS256", "kid": "abc123", "typ": "JWT" }获取并检查 JWK Set:访问 OIDC Provider 的 JWKS 端点(通常是
/.well-known/jwks.json或/oauth2/certs),检查返回的 JWK Set。curl -s https://your-oidc-provider/.well-known/jwks.json | jq .确认返回中是否有
kid为abc123、kty为RSA、alg为RS256(或兼容)的密钥。检查密钥元数据匹配情况:逐一比对以下字段:
kid:必须完全匹配(区分大小写)kty:RSA Token 需要RSA,EC Token 需要ECalg:如果 JWK 声明了alg,必须与 Token Header 的alg兼容use:应为sig(签名用途)crv(仅 EC 密钥):必须与算法匹配(如ES256→P-256)
检查 Elasticsearch 的 JWT Realm 配置:查看
elasticsearch.yml或相关安全配置中 JWT Realm 的allowed_jwks或pemtrustedcas_filepath等设置,确认没有额外限制条件。确认 JWK 缓存是否刷新:Elasticsearch 会缓存 JWK Set,如果刚做过密钥轮换,等待缓存过期(通常 1-24 小时,取决于 OIDC Provider 的
Cache-Control头)或重启节点强制刷新。
排查时需要注意的问题 #
- 不要只看 JWK Set 的首次返回结果,某些 OIDC Provider 会根据请求头或负载返回不同的 JWK Set,需要确认 Elasticsearch 实际获取到的内容。
- 如果生产环境使用了多个 JWT Realm 或多种认证方式,要确认请求走的是预期的 Realm。
- 密钥轮换期间,新旧密钥可能同时存在,但 Token 可能只使用新密钥签名,需确认 JWK Set 已包含新密钥。
4. 如何解决这个错误 #
常用修复思路 #
- 确保 JWK Set 包含匹配的密钥:在 OIDC Provider 侧确认 JWK Set 已发布包含正确
kid、算法和用途的密钥。如果刚完成密钥轮换,确认新密钥已发布到 JWKS 端点。 - 保持 Token Header 和 JWK 元数据一致:修正 Token 签发配置,确保
kid、alg与 JWK Set 中的元数据匹配。如果无法修改 Token 签发方,则调整 JWK Set 以兼容现有 Token。 - 调整不合理的安全策略:如果错误是因为密钥强度不达标,可以升级密钥(如 RSA 2048 → 4096),或适当调整 Elasticsearch 的密钥强度要求(不推荐降低安全要求)。
- 修正 JWK Set 配置错误:检查 JWK Set JSON 是否格式正确、
keys数组是否非空、各字段是否完整。
示例:一个正确的 JWK Set 片段
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"kid": "abc123",
"alg": "RS256",
"n": "0vx7agoebGcQ...",
"e": "AQAB"
}
]
}
后续注意事项与推荐建议 #
- 在 OIDC Provider 侧建立密钥轮换机制,并确保新密钥在旧密钥停用前就已发布到 JWKS 端点,避免轮换间隙验签失败。
- 为 JWT 认证配置监控,当验签失败率上升时及时告警,便于在密钥轮换或配置变更后快速发现问题。
- 在测试环境验证密钥轮换流程,确认 Elasticsearch 能正确获取并缓存新的 JWK Set,再在生产环境执行轮换。
借助 INFINI 产品提升排障效率 #
- INFINI Console 适合查看 Elasticsearch 安全日志、认证失败趋势和请求画像,帮助快速判断是 JWK 配置问题还是 Token 签发问题。
- INFINI Gateway 适合部署在 Elasticsearch 前面做请求观测、JWT 校验和流量治理,可以在网关层提前拦截不合法的 Token,减少后端集群的验签压力。
- 如果需要长期治理,建议把 JWT 认证日志、JWK 获取记录、验签失败原因统一接入监控面板,缩短从"发现问题"到"定位根因"的时间。
5. 小结 #
Verify failed because all ... provided JWKs were filtered. 这个异常说明候选密钥在"筛选阶段"就全部失效,而非签名值本身不对。排查时要同时关注 Token Header 的 kid 和 alg、JWK Set 的元数据完整性,以及 Elasticsearch 当前的校验策略。只要按 Token → JWK → 配置链路逐一核对,大多数密钥不匹配问题都可以快速定位并修复。
相关错误 #
附:日志上下文 #
下面保留当前页面中的源码或日志片段,便于继续结合异常调用栈定位问题:
throw new ElasticsearchSecurityException(
"Verify failed because all {} provided JWKs were filtered.",
providedJwks.size()
);





