エラーメッセージ
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」の前にスペースが 2 つあることに注意してください)。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 は入力に含まれるドットを数えましたが、署名付きトークン(JWS)の 2 個にも、暗号化されたトークン(JWE)の 4 個にも当てはまりませんでした。この文字列はコンパクト形式の JWT ではないため、鍵も署名も参照されていません。「Found: 0」は通常、セッション ID や API キーのような不透明な値を意味します。空白文字に関するエラーであれば、`Bearer` プレフィックスや改行がまだ付いたままです。
発生する理由
値が JWT ではない
よくある不透明な OAuth トークン、セッション Cookie、API キー、`"null"`、空のヘッダー値などです。ドットで区切られた base64url のセグメントからなる文字列だけが、コンパクト形式の JWT です。
Authorization ヘッダーがそのまま渡された
よくある`Bearer eyJ…` にはスペースが含まれており、JJWT はドットを数える前にこれを拒否します。先にスキームを取り除いてください。
トークンの一部が欠けている
ときどきドットの位置で切れたトークン(ログの切り詰め、サイズ制限のある列、手作業でのコピー)では、ドットが 1 つしか残りません(「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 のリソースサーバー機能は、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(); }