Сообщение об ошибке
Invalid compact JWT string: Compact JWSs must contain exactly 2 period characters, and compact JWEs must contain exactly 4. Found: 0
Также выводится как
- io.jsonwebtoken.MalformedJwtException: Invalid compact JWT string: Compact JWSs must contain exactly 2 period characters, and compact JWEs must contain exactly 4. Found: 1
- io.jsonwebtoken.MalformedJwtException: Compact JWT strings may not contain whitespace.
- io.jsonwebtoken.MalformedJwtException: JWT strings must contain exactly 2 period characters. Found: 0
Формулировка JJWT 0.12+ (обратите внимание на два пробела перед «Found»). JJWT 0.11 и более ранние версии писали «JWT strings must contain exactly 2 period characters. Found: N». Входные данные с пробелами отклоняются раньше с сообщением «Compact JWT strings may not contain whitespace.»
JJWT (Java) — io.jsonwebtoken.MalformedJwtException from JwtParser.parse*
Что это означает
JJWT посчитал точки во входных данных и не нашёл ни 2 точек, как в подписанном токене (JWS), ни 4, как в зашифрованном (JWE). Строка — не компактный JWT, поэтому ни ключ, ни подпись не проверялись. «Found: 0» обычно означает непрозрачное значение, например идентификатор сессии или API-ключ; жалоба на пробельные символы означает, что к токену всё ещё присоединён префикс `Bearer` или перевод строки.
Почему это происходит
Значение — не JWT
частоНепрозрачные токены OAuth, cookie сессии, API-ключи, `"null"` или пустое значение заголовка. Компактный JWT — это только строка из сегментов base64url, разделённых точками.
Заголовок Authorization передан целиком
частоВ `Bearer eyJ…` есть пробел, и JJWT отклоняет строку ещё до подсчёта точек. Сначала уберите схему аутентификации.
Часть токена отсутствует
иногдаЕсли токен обрезан по точке (усечение в журналах, столбцы ограниченного размера, ручное копирование), в нём остаётся одна точка («Found: 1»).
Как исправить
- 1.
Правильно извлекайте токен из заголовка
Принимайте только `Bearer <token>` (схема без учёта регистра), а на всё остальное отвечайте 401.
java static String bearerToken(String authorizationHeader) { if (authorizationHeader == null) return null; String[] parts = authorizationHeader.trim().split("\\s+", 2); if (parts.length != 2 || !parts[0].equalsIgnoreCase("Bearer")) return null; return parts[1]; } - 2.
Или доверьте извлечение фреймворку
Поддержка сервера ресурсов в Spring Security извлекает и проверяет bearer-токены и формирует ответы об ошибках по RFC 6750.
java // Spring Security resource server: the BearerTokenAuthenticationFilter extracts the token for you. @Bean SecurityFilterChain api(HttpSecurity http) throws Exception { return http .authorizeHttpRequests(auth -> auth.anyRequest().authenticated()) .oauth2ResourceServer(oauth -> oauth.jwt(Customizer.withDefaults())) .build(); }