How to Decode a JWT in Java and Spring Boot (With and Without jjwt)
- Manual Java decode using Base64 — no Maven dependency
- Using jjwt library to parse and read JWT claims
- Spring Boot: reading JWT claims from SecurityContext
- Handling the base64url padding issue in Java
Table of Contents
Decoding a JWT in Java does not require a library — the standard Base64 class handles it. For production Spring Boot apps, jjwt or Spring Security OAuth2 resource server are the preferred choices. Here is how each approach works.
Decode JWT Payload in Plain Java — No Maven Dependency
import java.util.Base64;
import org.json.JSONObject;
public class JwtDecoder {
public static JSONObject decodePayload(String token) {
String[] parts = token.split("\.");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid JWT format");
}
String payload = parts[1];
// base64url uses - and _ instead of + and /
payload = payload.replace('-', '+').replace('_', '/');
// Add padding
int mod = payload.length() % 4;
if (mod == 2) payload += "==";
else if (mod == 3) payload += "=";
byte[] decoded = Base64.getDecoder().decode(payload);
return new JSONObject(new String(decoded, java.nio.charset.StandardCharsets.UTF_8));
}
public static void main(String[] args) {
JSONObject claims = decodePayload(token);
String sub = claims.getString("sub");
long exp = claims.getLong("exp");
System.out.println("User: " + sub);
System.out.println("Expires: " + new java.util.Date(exp * 1000L));
}
}
This approach uses only standard library classes. Add the org.json dependency for the JSONObject parser, or use Jackson's ObjectMapper if you already have it in your project.
Decode JWT with jjwt in Spring Boot
Add to pom.xml:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.5</version>
<scope>runtime</scope>
</dependency>
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
// Parse and verify (HS256 with secret key)
Claims claims = Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secretKey.getBytes()))
.build()
.parseSignedClaims(token)
.getPayload();
String sub = claims.getSubject();
String email = claims.get("email", String.class);
Date expiry = claims.getExpiration();
Sell Custom Apparel — We Handle Printing & Free Shipping
Reading JWT Claims in Spring Security
When using Spring Boot as an OAuth2 resource server, JWT claims are automatically decoded and available in the security context:
// application.properties
spring.security.oauth2.resourceserver.jwt.issuer-uri=https://YOUR-ISSUER.com
// Controller — claims auto-decoded from Bearer token
@GetMapping("/user")
public ResponseEntity<?> getUser(
@AuthenticationPrincipal Jwt jwt) {
String userId = jwt.getSubject();
String email = jwt.getClaimAsString("email");
List<String> roles = jwt.getClaimAsStringList("roles");
return ResponseEntity.ok(new UserResponse(userId, email));
}
Spring Security validates the signature against the issuer's JWKS endpoint automatically. You get decoded, verified claims without writing any JWT parsing code.
Common Java JWT Decoding Errors and Fixes
- IllegalArgumentException: Illegal base64 character: You are using Base64.getDecoder() on a base64url-encoded string. Replace
-with+and_with/before decoding, or use Base64.getUrlDecoder() instead. - ArrayIndexOutOfBoundsException on split: The token does not have three parts. Check that the full token (including the signature) was passed — some logging or copy-paste truncates the signature.
- JSONException: Missing key: The claim you are accessing does not exist in the payload. Use
claims.optString("key")or check withclaims.has("key")before accessing. - ExpiredJwtException (jjwt): The token's exp has passed. The exception is thrown during parsing — catch it separately from other JwtExceptions to return a 401 with a useful message.
Check Your Token Claims Before Coding
Paste your JWT above to confirm claim names and structure — faster than running a Spring Boot app to debug.
Open Free JWT DecoderFrequently Asked Questions
Can I use Base64.getUrlDecoder() instead of Base64.getDecoder() for JWTs?
Yes — Base64.getUrlDecoder() handles the - and _ character substitution automatically. You still need to add padding manually before passing to getUrlDecoder().decode().
What is the difference between jjwt and java-jwt (Auth0)?
Both are popular Java JWT libraries. jjwt is the most widely used. Auth0's java-jwt (com.auth0:java-jwt) has a slightly different API but similar capabilities. Either works for decode and verification.
How do I decode a JWT without verifying the signature in jjwt?
Use Jwts.parser().build().parseUnsecuredClaims(token) for tokens with "alg": "none", or manually decode the base64 payload as shown in the manual approach above. jjwt 0.12+ requires explicit steps to skip verification.

