Generate JWT. Add and Validate Custom Claims.

Creating and Validating JWT in Spring Boot application

In this tutorial, you will learn how to generate a custom JSON Web Token(JWT) and how to add and validate custom JWT Claims using the io.jsonwebtoken library. The JWT token generated in this tutorial will be signed with a SecretKey, making it secure and tamper-proof.

Also, this tutorial assumes you have prior knowledge of creating Java projects using Maven. If not, I recommend you read the following tutorial Create Java Project with Maven.

Add io.jsonwebtoken Libraries to POM.XML

Adding JWT support to your Java application is very simple. All you need to do is include the io.jsonwebtoken library by adding the following Maven dependencies to your pom.xml file.

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.12.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.12.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-jackson -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.12.3</version>
    <scope>runtime</scope>
</dependency>

Generate Simple JWT

To generate a new JWT, I will use the Jwts.builder(), which comes with the JJWT dependency we have added to a pom.xml file.

	    
String tokenSecret = environment.getProperty("token.secret");
byte[] secretKeyBytes = tokenSecret.getBytes();
SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes);  
 
Instant now = Instant.now();

String token = Jwts.builder()
	       .subject(userName)
	       .expiration(Date.from(now.plusMillis(Long.parseLong(environment.getProperty("token.expiration_time")))))
	       .issuedAt(Date.from(now))
	       .signWith(secretKey)   
	       .compact();

Here’s a step-by-step breakdown of how the code works:

Secret Key Preparation

    • String tokenSecret = environment.getProperty("token.secret");: This line fetches a secret string from the environment properties, which is crucial for signing the JWT to ensure its authenticity. It is an alpha-numeric string of characters that you can create yourself. But it is needed to compute the value of SecretKey. The longer the value of the token secret, the more secure SecretKey you will get.
    • byte[] secretKeyBytes = tokenSecret.getBytes();: This line converts the secret string into a byte array, which is a needed format for the next step.
    • SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes);: Here, a SecretKey object is created from the byte array using the Keys.hmacShaKeyFor method. This SecretKey object will be used to sign the JWT.

JWT Construction

    • String token = Jwts.builder(): This line initializes the process of building a JWT.
    • .subject(userName): The subject method is called with userName as an argument to set the subject of the JWT, which typically is the user the token represents. Basically, this value is a unique user identifier, which is usually a user name or a user ID.
    • .expiration(Date.from(now.plusMillis(Long.parseLong(environment.getProperty("token.expiration_time"))))): This line sets the expiration time of the JWT. The value of token.expiration_time is specified in milliseconds and is being read from the Environment object of the Spring Boot application.
    • .issuedAt(Date.from(now)): The issuedAt method is called with the current time as an argument to set the issuance time of the JWT.
    • .signWith(secretKey): The signWith method is called with the SecretKey object as an argument to sign the JWT, ensuring its authenticity and integrity.
    • .compact();: Lastly, the compact method is called to finalize the JWT construction and return it as a compact, URL-safe string, which is stored in the token variable.

This generated JWT can now be used for secure communication, carrying the subject, issued time, and expiration time as claims.

Complete Code Example

/**
 *
 * @author sergeykargopolov
 */
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.time.Instant;
import java.util.Base64;
import java.util.Date;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

public class JWTExample {
   private static final String subject = "user";
   private static final String tokenSecret = "snjdu438fkdj38fdmcv7dm3ckvhrsnjdu438fkdj38fdmcv7dm3ckvhr";

   public static void main(String[] args) {
 
        byte[] secretKeyBytes = tokenSecret.getBytes();
        SecretKey secretKey = Keys.hmacShaKeyFor(secretKeyBytes);

        Instant now = Instant.now();
        Instant expiration = now.plusSeconds(3600); // expires in 1 hour
        Date expDate = Date.from(expiration);

        String token = Jwts.builder()
                .subject(subject)
                .expiration(expDate)
                .issuedAt(Date.from(now))
                .signWith(secretKey)
                .compact();

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

If you run the above application, you will get the JWT printed:

JWT Token: eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNjc2MjQ5MTA0LCJleHAiOjE2NzYyNTI3MDR9.8n2MF3c17IrVrK60ZMq3_6JjbQ-R6coXe0L_wK0rLD0Dt0rITOeu_LMXXV43KRGroeeo9IrrUaI4CoO-qgu4EQ

The JWT is Base64 encoded, and you can easily decode it with any Base64 decoder online tool. Because the content of JWT can be easily decoded, you should never place a user’s sensitive information into the JWT token. For example, never include the user’s password or token secret with which the JWT was signed in the body of the JWT token.

Validate JWT

To validate the JWT we will need to use the same signing key with which the JWT was earlier signed. If the token secret is not correct, the following error will be thrown when JWT is validated: “io.jsonwebtoken.SignatureException: JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.” You can use the following code snippet to validate JWT and read the subject value.


String tokenSecret = "dgjorufhrsnjdu438fkdj38fdmcv7dm3ckvhrsnjjuwelueivhe848fhedldh5ndk";
byte[] secretKeyBytes = tokenSecret.getBytes();
SecretKey key = Keys.hmacShaKeyFor(secretKeyBytes);
JwtParser parser = Jwts.parser()
                .verifyWith(key)
                .build();

Claims claims = parser.parseSignedClaims(token).getPayload();

String extractedSubject = (String) claims.get("sub");
System.out.println(extractedSubject);


In this code, the JwtParser is first configured to use the specified secret key to verify the signature of the JWT. The JWT is then parsed using the parse method, and the subject is extracted from the claims using the getSubject method. The extracted subject is then printed to the console.

Add Custom Claims to JWT

Claims live in the Body of JWT.  JWT Claims are pieces of information that are asserted to the subject and are key-value pairs. There are claims that are reserved to ensure interoperability with third-party or external applications, and there are custom claims that can be added to JWT by you. If needed, you can add additional pieces of information(custom claims) about the subject or the user for which the JWT is created.

Reserved Claims

Let’s have a look at the list of reserved claims. iss – Issuer, sub – Subject, aud – Audience, exp – Expiration, nbf – Not Before, iat – Issued At, jti – JWT ID

Custom Claims

Custom claims are custom key-value pairs that you can add to the body of JWT. It can be a user Role or a Privilege; it can be the user’s department at work or anything else you need to add to JWT. For example, in the below code snippet, I am adding two custom claims to JWT, which are the user’s Role and Department at work.

        // Generate GWT
        String token = Jwts.builder()
                .subject(subject)
                .expiration(expDate)
                .issuedAt(Date.from(now))
                .claim("Role", "Admin") 
                .claim("Department", "Product development")
                .signWith(secretKey)
                .compact();

In the above code example, I have added two custom claims: Role and Department. If needed, you can add more claims to the body of JWT. Just remember not to add sensitive information like a user password or token secret. JWT claims can be decoded and viewed.

Read Claims from JWT

To read the custom Claims from the body of the JWT token, you can use the following code snippet.

String tokenSecret = "dgjorufhrsnjdu438fkdj38fdmcv7dm3ckvhrsnjjuwelueivhe848fhedldh5ndk";
byte[] secretKeyBytes = tokenSecret.getBytes();
SecretKey key = Keys.hmacShaKeyFor(secretKeyBytes);
        
JwtParser parser = Jwts.parser()
                .verifyWith(key)
                .build();
    
Claims claims = parser.parseSignedClaims(token).getPayload();
        
String extractedSubject = claims.getSubject();
System.out.println(extractedSubject);
 
// Reading Reserved Claims  
System.out.println("Subject: " + claims.get("sub"));
System.out.println("Expiration: " + claims.get("exp"));

// Reading Custom Claims       
System.out.println("Role: " + claims.get("Role"));
System.out.println("Department: " + claims.get("Department"));

Below is a short explanation of the code snippet that parses JWT.

Initializing a JWT Parser

    • JwtParser parser = Jwts.parser(): This line initializes a JWT parser, a tool for reading and verifying JWTs.
    • .verifyWith(key): This method call configures the parser to use the SecretKey created earlier to verify the JWT’s signature, ensuring it hasn’t been tampered with. To verify JWT, you need to create a SecretKey using exactly the same token secret value with which this JWT was earlier created.
    • .build();: This method call finalizes the configuration and creates a ready-to-use JwtParser object.

Parsing the JWT

    • Claims claims = parser.parseSignedClaims(token).getPayload();: This line does a lot. It tells the parser to read and verify the JWT contained in the string token, extract the payload which contains the claims, and store these claims in a Claims object.

Extracting the Subject Claim

    • String extractedSubject = (String) claims.get("sub");: This line extracts the subject claim (abbreviated as “sub”) from the Claims object and stores it as a string in the extractedSubject variable.

Remember that JWT is a Base64 encoded string and can be easily decoded. Therefore, you should not put into Claims any sensitive user details. Even though the information in Claims cannot be altered, this information can be viewed by the Base64-decoding JWT token.

Conclusion

I hope this tutorial was helpful to you. If you want to learn more about Spring Boot and Spring Security check out my other tutorials on this blog. If you are interested in learning about Microservices and Spring Cloud, then check out this page: Spring Cloud. If you enjoy learning by following step-by-step online video courses, then have a look at the below list of online video courses that teach Spring Security. One of them might be very helpful.

Leave a Reply

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