错误信息
Signed JWT rejected: Invalid signature
也可能输出为
- com.nimbusds.jose.proc.BadJWSException: Signed JWT rejected: Invalid signature
- org.springframework.security.oauth2.jwt.BadJwtException: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Invalid signature
Nimbus JOSE + JWT (Java) — BadJWSException from DefaultJWTProcessor; also surfaced by Spring Security's NimbusJwtDecoder
含义
Nimbus 为这个令牌选出了一个或多个候选密钥(依据 `kid`、`alg` 和密钥类型),但用其中任何一个密钥验证签名都没有通过。选择这一步是成功的——否则你看到的会是“Another algorithm expected, or no matching key(s) found”——因此,要么你信任的密钥与签名该令牌的密钥不同,要么令牌被修改过。Spring Security 通过 `NimbusJwtDecoder` 报告同样的情况,并加上前缀“An error occurred while attempting to decode the Jwt:”。
原因
kid 相同,密钥不同
常见重新生成密钥时(按环境、按部署,或者在丢失旧密钥之后),`default`、`rsa1` 或日期这类密钥 ID 会被重复使用。验证方找到了 `kid` 正确的密钥,但这个密钥从未签名过该令牌。
资源服务器信任的是另一个颁发者的密钥
常见`jwk-set-uri` 指向了另一个领域(realm)、租户或环境,而不是签发这些令牌的那一个。
各服务之间的 HMAC 密钥不一致
偶见使用 HS256 和 `MACVerifier` 时,共享密钥的字节只要有任何差异(编码、末尾换行符),所有签名都会无效。
令牌在签名后被修改过
偶见头部或载荷被代理重新编码,或者令牌是由不同令牌的各部分拼接而成的。
修复方法
- 1.
配置颁发者,而不是手动复制的密钥集
配置 `issuer-uri` 后,Spring Security 会从颁发者的发现文档中读取 `jwks_uri`,并同时校验 `iss`。
yaml # Spring Boot resource server: derive the JWK Set from the issuer's discovery document spring: security: oauth2: resourceserver: jwt: issuer-uri: https://issuer.example.com/realms/main - 2.
比较指纹,而不只是 kid
RFC 7638 指纹标识的是实际的密钥材料。如果已发布密钥的指纹与颁发者签名所用密钥的指纹不同,说明 `kid` 被重复使用了。
java SignedJWT jwt = SignedJWT.parse(token); System.out.println(jwt.getHeader().getKeyID() + " " + jwt.getHeader().getAlgorithm()); JWKSet set = JWKSet.load(new java.net.URI("https://issuer.example.com/.well-known/jwks.json").toURL()); for (JWK key : set.getKeys()) { System.out.println(key.getKeyID() + " " + key.getAlgorithm() + " thumbprint=" + key.computeThumbprint()); }