JSON Web Token (JWT) and Java

JSON Web Token (JWT) and Java

jwt json web token

Overview

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed.

One of the mostly used use-cases for JWT is authorization. A client authenticates and a server returns a generated token in response. The client stores the token and sends it with every further request.

JSON Web Token (JWT) structure

JSON Web Token consists of three parts separated by dots, which are:

  • Header

The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA.

{
  "alg": "HS256",
  "typ": "JWT"
}
  • Payload

Payload contains the claims that represent information about entity (typically, the user) and additional data. Some of them are pre-defined: iss (issuer), exp (expiration time), sub (subject), aud (audience) and others. Additionally, there could be custom claims to share information between parties.

  • Signature

The signature is created signing the encoded header, the encoded payload, a secret and the algorithm specified in the header.

<SigningAlgorithm>(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

Header and Payload are Base64-encoded. Also, their encoded representation is used for signature creation.

The final JWT can look like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

There is a great service that allows to easily view the contents of a token – jwt.io. Just paste the token and look at the contents:

jwt.io token

As you can see, having the token, it’s easy to get access to its contents. The signature allows to validate a token on the server-side, but it does not encrypt the content. So, you should either not put a sensitive information inside or additionally encrypt the token.

JWT and Java

To create, parse and validate JWT, the following dependecy has to be included:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.5.1</version>
</dependency>

Let’s create a utility class able to generate, parse and validate tokens:

public class TokenProvider {
    private static final Logger logger = LoggerFactory.getLogger(TokenProvider.class);
    
    private static final String TOKEN_SECRET = "926D96C90030DD58429D2751AC1BDBBC";
    private static final long TOKEN_EXPIRATION_IN_MS = 864000000L;
    
    private  static final String ROLE_PARAM = "role";

    static String createToken(final User user) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + TOKEN_EXPIRATION_IN_MS);

        return Jwts.builder()
                .setSubject(user.getFullName())
                .claim(ROLE_PARAM, user.getRole())
                .setIssuedAt(new Date())
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, TOKEN_SECRET)
                .compact();
    }

    static User getUserFromToken(final String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(TOKEN_SECRET)
                .parseClaimsJws(token)
                .getBody();

        return new User(claims.getSubject(), (String) claims.get(ROLE_PARAM));
    }

    static boolean validateToken(final String authToken) {
        try {
            Jwts.parser()
                .setSigningKey(TOKEN_SECRET)
                .parseClaimsJws(authToken);
            
            return true;
        } catch (SignatureException ex) {
            logger.error("Invalid JWT signature");
        } catch (MalformedJwtException ex) {
            logger.error("Invalid JWT token");
        } catch (ExpiredJwtException ex) {
            logger.error("Expired JWT token");
        } catch (UnsupportedJwtException ex) {
            logger.error("Unsupported JWT token");
        } catch (IllegalArgumentException ex) {
            logger.error("JWT claims string is empty.");
        }
        return false;
    }

    public static void main(String[] args) {
        String token = TokenProvider.createToken(new User("John Doe", "ADMIN"));

        System.out.println("Token: " + token);

        boolean isValid = TokenProvider.validateToken(token);
        System.out.println("Token is valid: " + isValid);

        User user = TokenProvider.getUserFromToken(token);
        System.out.println(user);
    }
}

TOKEN_EXPIRATION_IN_MS constant specifies how long the token will be valid.

TOKEN_SECRET constant contains a secret to generate/parse/validate a token. The secret is what protects us from an attacker. An attacker could easily create a header and payload, but he can’t reproduce the signature for his token content because it’s based on a combination of a token content and the secret which he doesn’t know.

Leave a Reply

Your email address will not be published. Required fields are marked *