Сообщение об ошибке
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` или даты используются повторно, когда ключ генерируют заново (для каждого окружения, при каждом развёртывании, после потери старого ключа). Проверяющая сторона находит ключ с нужным `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()); }