Using Nimbus JOSE + JWT in Spring Applications
Want to learn more about the difference between using Nimbus JOSE and JWT tokens in Spring Applications? Click here to learn more!
Join the DZone community and get the full member experience.Join For Free
JWTs could be very useful in RESTful Web Services — not only for stateless authentication, but for all the purposes that require tokens — e.g. email verification and forgot-password. In this post, we'll discuss why and how to use the Nimbus JOSE + JWT library for creating and parsing JWT (JWE) tokens.
For code examples, we’ll refer to Spring Lemon. If you haven’t heard of Spring Lemon, you should give it a look. It’s a library encapsulating the sophisticated non-functional code and configuration that’s needed when developing real-world RESTful web services using the Spring framework and Spring Boot.
JWT in a Nutshell
There’s already a lot of material available on JWT. So, instead of repeating those here, let's just summarize it as plainly as possible.
A JWT is a URL-safe token with some data embedded in it. The data would be signed/encrypted, so that malicious guys can’t alter it (or create another token with altered data). There are two kinds of JWTs — JWS and JWE.
JWS tokens will have their data signed but not encrypted. That means that the data can be parsed by anyone. For example, these can be used as authentication tokens where the front-end or any client needs to read the data.
JWE tokens instead will have the data encrypted so that no one (except the creator or someone having the secret key) can parse it.
Why Nimbus JWT
Java has two popular open source libraries for JWT creation and parsing: JJWT and Nimbus JOSE + JWT. Between these, JJWT is simple and easy to use. In fact, most articles on the Internet that dicuss how to use JWT with Spring use the JJWT library.
So, why bother using the Nimbus JWT?
For a couple of reasons. First, Nimbus JWT is comprehensive. It has many useful features that are not found in JJWT. For example, JJWT supports only JWS, but Nimbus supports both JWS and JWE. JWE is essential for creating tokens to be sent through mail (e.g. forgot-password tokens).
Secondy, Spring Security 5 itself uses Nimbus JWT — its dependencies like spring-security-oauth2-client and spring-security-oauth2-jose include nimbus-jose-jwt.
So, when using Spring Security 5, it’s natural to prefer Nimbus JWT instead of any other library.
How to Use Nimbus JWT
Nimbus JWT supports multiple algorithms for signing and encrypting tokens. In this post, we’ll discuss creating/parsing JWE tokens using a shared key, which would fit into most use cases when developing stateless REST Web Services — e.g. authorization, email validation, forgot-password etc.
So, let’s see how to use it. I’ll be pasting in some code snippets as examples, but for a detailed reference, you can refer to the JwtService class of Spring Lemon and the Spring Framework Recipes For Real World Application Development book.
Creating a Token
A JWT will have multiple parts. One of those is the payload, which contains the actual data to carry.
Creating the Payload
Each piece of the data in the payload is called a claim. For example, if you’d like to pass an email and a name, first build a
ClaimsSet with two claims:
JWTClaimsSet claims = new JWTClaimsSet.Builder() .claim("email", "firstname.lastname@example.org") .claim("name", "Sanjay Patel") .build();
Tip: You can use the standard JWT claims — such as subject, audience or expiration time — when possible. In fact, Nimbus classes, such as the builder above, have special methods supporting those. See the JwtService class of Spring Lemon for examples.
Then, create the payload as below:
Payload payload = new Payload(claims.toJSONObject());
Creating the Header
Another part of a JWT would be the header, which contains the encryption algorithm and method. Here is how you could create one:
JWEHeader header = new JWEHeader(JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256);
The above says that we are going to use direct encryption with A128CBC_HS256 algorithm. So, the next step is to create an encrypter for it.
Creating the Encrypter
The encrypter can be created as below:
String secret = “841D8A6C80CBA4FCAD32D5367C18C53B”; byte secretKey = secret.getBytes(); encrypter = new DirectEncrypter(secretKey);
secret above is an aes-128-cbc key, generated using an online utility.
Note that the encrypter above could be a global one; you don’t need to create an encrypter per token.
Creating the Token
Now that you have the header, payload, and an encrypter, the next steps would be create a JWE object, encrypt it, and then serialize it to produce the desired token:
JWEObject jweObject = new JWEObject(header, payload); jweObject.encrypt(encrypter); String token = jweObject.serialize();
So, your token is ready. Give it to whoever wants it!
Parsing a Token
Let’s now see how to parse tokens. For that, we’d first need to configure a JWT Processor.
Configuring a JWT Processor
Configuring our JWT Processor can be done as below:
ConfigurableJWTProcessor<SimpleSecurityContext> jwtProcessor = new DefaultJWTProcessor<SimpleSecurityContext>(); JWKSource<SimpleSecurityContext> jweKeySource = new ImmutableSecret<SimpleSecurityContext>(secretKey); JWEKeySelector<SimpleSecurityContext> jweKeySelector = new JWEDecryptionKeySelector<SimpleSecurityContext>(JWEAlgorithm.DIR, EncryptionMethod.A128CBC_HS256, jweKeySource); jwtProcessor.setJWEKeySelector(jweKeySelector);
secretKey above must be the same one that we used earlier.
Parsing the Claims
jwtProcessor can then be used to parse the claims out of our tokens, which is shown below:
JWTClaimsSet claims = jwtProcessor.process(token, null); String email = (String) claims.getClaim(“email”); String name = (String) claims.getClaim(“name”);
So, this is a way we can use Nimbus JWTs in our applications. It does look a little harder than JJWT, but this is non-functional stuff that you only need to code once to receive its benefits all the time. (You, of course, would need to maintain it as new versions of Nimbus JWT comes out, but you can use a high level library like Spring Lemon to avoid coding such non-functional stuff.)
Published at DZone with permission of Sanjay Patel. See the original article here.
Opinions expressed by DZone contributors are their own.