Saltar al contenido
AldeaCode Logo
Decodificador JWT / Java Desarrollador 100% local

Decodificar JWT en Java: jjwt, java-jwt o Base64 sin dependencias

Un JWT en Java es el mismo envoltorio del RFC 7519 que en cualquier otro sitio: header, payload, firma, separados por puntos y cada parte en Base64URL. La decisión es qué librería usar y si realmente quieres verificar o solo inspeccionar.

Decodificar no es verificar

Partir el token por los puntos y hacer Base64URL-decode del segmento del medio te da los claims. Eso es decodificar. La firma se queda intacta, así que cualquier manipulación pasa sin detectarse.

En código de producción, decodificar sin verificar es una vulnerabilidad. El exploit clásico cambia el alg del header a none, quita la firma, y cualquier servicio que confíe en los claims decodificados acepta el forge. Reserva decode para depuración, logs y tests. Usa verify para todo lo que conceda acceso.

io.jsonwebtoken (jjwt) es la opción común

io.jsonwebtoken:jjwt-api más los artefactos runtime -impl y -jackson es el stack JWT más extendido en Java. La API separa parseo y validación, lo que hace la frontera de seguridad explícita:

Jws<Claims> jws = Jwts.parserBuilder()
    .setSigningKey(secretKey)
    .build()
    .parseClaimsJws(token);
Claims claims = jws.getBody();

Importar claves RSA o EC es verboso, pero el código resultante rechaza alg: none por defecto y se niega a aceptar tokens cuyo algoritmo del header no coincida con el tipo de clave. Las dos cosas eran fallos que algunas librerías antiguas dejaban pasar.

com.auth0:java-jwt como alternativa

java-jwt de Auth0 es la otra opción muy usada. El builder fluent tiene otro estilo:

DecodedJWT jwt = JWT.require(Algorithm.HMAC256(secret))
    .build()
    .verify(token);
String userId = jwt.getClaim("sub").asString();

También expone JWT.decode(token) para inspección, que devuelve un DecodedJWT sin verificar la firma. Útil en logs, peligroso en rutas de producción. Misma advertencia que antes: en cuanto un controller confíe en esos claims, tienes un bug.

Decode sin dependencias para debugging

Si solo quieres inspeccionar un token de una respuesta curl, java.util.Base64.getUrlDecoder() y String.split("\\.") bastan. Sin coordenadas Maven, sin dependencias transitivas. El ejemplo de abajo imprime el header y el payload de cualquier JWT compacto.

Ejemplo completo

java
import java.nio.charset.StandardCharsets;
import java.util.Base64;

public class JwtInspector {
    public static void main(String[] args) {
        String token = "eyJhbGciOiJIUzI1NiJ9."
                     + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFkYSJ9."
                     + "5mhBHqs5_DTLdINd9p5m7ZJ6XD0Xc55kIaCRY5r6HRA";

        String[] parts = token.split("\\.");
        if (parts.length < 2) {
            throw new IllegalArgumentException("No es un JWT");
        }

        Base64.Decoder dec = Base64.getUrlDecoder();
        String header = new String(dec.decode(parts[0]), StandardCharsets.UTF_8);
        String payload = new String(dec.decode(parts[1]), StandardCharsets.UTF_8);

        System.out.println("Header:  " + header);
        System.out.println("Payload: " + payload);
        // Código de producción: ahora verifica la firma con jjwt o java-jwt.
    }
}

¿Solo necesitas el resultado?

Cuando estás depurando y quieres ver el contenido de un JWT sin escribir una clase Main, pégalo en el decodificador JWT de aldeacode.com. Header y payload aparecen al instante, la firma se queda opaca a propósito, y nada sale de tu navegador. Úsalo como paso de inspección antes de pasar a verify en código real.

Abrir Decodificador JWT →

Preguntas frecuentes

¿Por qué jjwt parte la API en api, impl y jackson?

Separar la API pública de la implementación permite a los mantenedores meter breaking changes en impl sin romper a quien compila contra la api. El artefacto jackson es el binding JSON; cambia a gson si ya tiras de Gson en otra parte y quieres una dependencia menos.

¿Es seguro el split Base64 manual para producción?

Para solo decodificar, sí. Para cualquier cosa que conceda acceso, no. El método manual no comprueba firma, ni exp, ni audience. Usa una librería real en cualquier flujo que dependa de que el token sea fiable.

¿Qué es el ataque alg=none y sigue siendo un riesgo?

Un atacante reescribe el alg del header a none, quita la firma y manda el token. Las librerías antiguas lo aceptaban. jjwt y java-jwt actuales lo rechazan por defecto. Si todavía mantienes código que llama a un parser sin especificar el algoritmo esperado, arréglalo hoy.