DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • What D'Hack Is DPoP?
  • Authentication With Remote LDAP Server in Spring Web MVC
  • Securing Spring Boot Microservices with JSON Web Tokens (JWT)
  • C# Applications Vulnerability Cheatsheet

Trending

  • The Human Side of Logs: What Unstructured Data Is Trying to Tell You
  • Secure by Design: Modernizing Authentication With Centralized Access and Adaptive Signals
  • Kullback–Leibler Divergence: Theory, Applications, and Implications
  • 5 Subtle Indicators Your Development Environment Is Under Siege
  1. DZone
  2. Coding
  3. Languages
  4. JSON Web Token in Action With JAX-RS

JSON Web Token in Action With JAX-RS

A quick introduction to JWT and how to use it with JAX-RS for authentication.

By 
Abhishek Gupta user avatar
Abhishek Gupta
DZone Core CORE ·
Mar. 30, 16 · Tutorial
Likes (6)
Comment
Save
Tweet
Share
73.7K Views

Join the DZone community and get the full member experience.

Join For Free

This post is about using JSON Web Token (JWT) with JAX-RS.

It covers:

  • A quick intro to JWT.
  • How to use it with JAX-RS (for authentication) with an example.

Also:

  • Demonstrates contextual state/information sharing feature provided by JAX-RS Request Filters and usage of custom Security Context in JAX-RS.
  • Uses the jose4j library for JWT creation and validation.

Brief Intro to JWT

  • A standard defined by RFC 7519
  • Used to exchange claims
  • Has a pre-defined structure

Anatomy of a JWT

It consists of three parts

  • Header: Consists of info like signature mechanism, token type etc.
  • Body (Claims): The meat of the payload.
  • Signature: Signature of the contents to protect against tampered/malicious JWTs.

These three components come together to form the actual token:

//header

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

//payload/claims

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

//the formula

encoded_part = base64Of(header) + "." base64Of(payload)
signature = signedUsingHS256WithSecret(encoded_part) //assume that algo is HS256 and secret key is 'secret'
JWT = encoded_part + "." + sigature

//the JWT ( notice the separator/period --> "." )

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 //base-64 encoded header
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9 //base-64 encoded payload
.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ //the signature

Benefits

  • Useful for implementing Stateless authentication.
  • Compact: less verbose compared to other counterparts like SAML).
  • Flexible: Although its backed by a standard, you are free to choose your signature, claim attributes etc.

Please Note That:

  • JWT is not only an authentication mechanism. It’s more about information exchange, and it’s usage is limited by your imagination.
  • It’s signed, notencrypted: its contents can be picked up over the wire if you do not secure your transport layer (e.g. using HTTPS).

Using JWT With JAX-RS

Let’s look at an example of how we might use JWT in a JAX-RS based application. As stated earlier, this sample uses JWT as a stateless authentication token. The process is split into distinct steps:

Getting Hold of the JWT

Why do we need a JWT in the first place? It is because the JAX-RS resource is protected and its access is dependent on the presence of a JWT token within the HTTP request (this is achieved by a JAX-RS filter)

Think of JWT as a proxy to the actual username/password (or any other authentication criteria) for your application. You need to actually authenticate using the method required by your application in order to get access to the JWT. In this example, a successfully executed HTTP Basic authentication is the gateway to the token

This is what happens

  1. The application executes a GET request to the URL http://<host&gt;:<port>/<context-root>/auth/token with the HTTP Authorization header containing user credentials
  2. HTTP Basic authentication kicks in. This is enforced by the web.xml (snippet below) which ensures that any request to the /auth/* is not allowed to pass unauthenticated
  3. In case of a successful authentication, the JWT is returned in the HTTP response header

Here is an excerpt form the web.xml

web_xml_snippet

Quick review of the JWT creation code and its result:

//exception handling excluded to avoid verbosity

RsaJsonWebKey rsaJsonWebKey = RsaKeyProducer.produce();

JwtClaims claims = new JwtClaims();
claims.setSubject("user1");

JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toJson());
jws.setKey(rsaJsonWebKey.getPrivateKey());
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);

String jwt = jws.getCompactSerialization();

//the encoded JWT

eyJhbGciOiJSUzI1NiJ9
.eyJzdWIiOiJ1c2VyMSJ9
.HG9GCQPuC6w6pulbYE2uurCzpEwoWvz_8Ps5ZjgtfomyY4LWacDEzlHLnyMj9H7aqgcePC7_4l2wDXQV-S0BQRsIZfJeUUmWxlTlLzvKZr_2eEx00YZPPFZNoFCfwB-ajLHLLenROy4aSjPo_Vg9o7N-p0DZ1yZQoJhkvoVJgkhX9FeAf65kIZkbuJC9dmVkzXSOpVf4GZeCpNDJJYSo6IAnL3UEoWek6V9BtWgV-a4xvydp7vxkdDXmzmalGLYuWbuVG7rWcbWwSfsg38iEG-mqptqA_Kzk1VmjwWNo_BfvLuzjzuosqi732-5SRzBP-2zqGghBqMYsGgkqkH2n7A

//human readable format

{
  "alg": "RS256" //header
}

{
  "sub": "user1" //claim payload
}

JWT in Action

  • The JWT is sent by app in the subsequent request for the JAX-RS resource i.e.http:<host>:<port>/<context-root>/resources/books
  • The JAX-RS Container Request Filter kicks in – it checks for the presence of the JWT , verifies it. The verification process implicitly checks for presence of the required claim attributes as well as the signature validation
@Priority(Priorities.AUTHENTICATION)
public class JWTAuthFilter implements ContainerRequestFilter{
  @Override
  public void filter(ContainerRequestContext requestContext) throws IOException {
        String authHeaderVal = requestContext.getHeaderString("Authorization");

            //consume JWT i.e. execute signature validation
            if(authHeaderVal.startsWith("Bearer")){
            try {
                validate(authHeaderVal.split(" ")[1]);
            } catch (InvalidJwtException ex) {
                requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
            }
            }else{
                requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
            }
  }

JWT verification snippet:

//excpetion hanlding omitted for brevity

RsaJsonWebKey rsaJsonWebKey = getCachedRSAKey(); //should be the same as the one used to build the JWT previously

JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                         .setRequireSubject() // the JWT must have a subject claim
                          .setVerificationKey(rsaJsonWebKey.getKey()) // verify the signature with the public key
                          .build(); // create the JwtConsumer instance

JwtClaims jwtClaims = jwtConsumer.processToClaims(jwt);
  • It allows the request to go through in case of successful verification. Otherwise, the filter returns a HTTP 401 Unauthorized response to the client.
  • A container response filter ensures that the JWT is added as a part of the response header again. It only does so when the JWT verification was successful. This is made possible using the contextual state/information sharing feature provided by JAX-RS Request Filters.
public class JWTResponseFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        System.out.println("response filter invoked...");
        if (requestContext.getProperty("auth-failed") != null) {
            Boolean failed = (Boolean) requestContext.getProperty("auth-failed");
            if (failed) {
                System.out.println("JWT auth failed. No need to return JWT token");
                return;
            }
        }

        List<Object> jwt = new ArrayList<Object>();
        jwt.add(requestContext.getHeaderString("Authorization").split(" ")[1]);
        responseContext.getHeaders().put("jwt", jwt);
        System.out.println("Added JWT to response header 'jwt'");

    }
}

Other Considerations

Choice of Claim Attributes

In this example, we just used the standard sub (subject) attribute in the claim. You are free to use others. I would highly recommend reading section 4 of the JWT RFC for deeper insight:

jwt_rfc_doc_claims_section

JWT Expiration

One should also consider expiring the JWT token after a finite time. You would need to:

  • Make use of the exp claim attribute (standard).
  • Think about refreshing the JWT token (after expiry).

Revisiting the Stateless Paradigm...

Although the initial authentication was executed using HTTP Basic, the application does not rely on a Session ID for authorizing subsequent requests from the same user. This has the following implications:

  • There is no need to store the session ID on the server side.
  • There is no need to sync this session ID to multiple application nodes in a cluster.

As stated above, JWT is helping us with Stateless authentication (it is not very different from the HTTP protocol itself):

  • Our JWT contains all the required data (claim) for the conversation (in this case authentication).
  • We pass the token with each HTTP request (only to access resources which are protected by the JWT to begin with).
  • The application does not need to repetitively authenticate the user (via the username-password combo).

Now We Can Scale :-)

You can have multiple instances (horizontallyscaled across various nodes/clusters) of your JAX-RS service and yet you need not sync the state of the token between various nodes. If a subsequent request goes to different node than the previous request, the authentication will still happen (provided you pass the JWT token).

Using the Sample Project

If you want to play around with the example, please follow these steps:

  • Get the maven project and build it (same old maven clean install)
  • Deploy on a Java EE 7 container
  • Tweak your server security options to protect it using a realm named file and ensure that you include the members under the users group. Both these constraints are dictated by the web.xml descriptor (see above) – you can also choose to modify it as per your choice. Here is a snapshot from the Payara server:

test-jwt-snippet-3

  • Issue a HTTP GET to http://localhost:8080/jax-rs-with-jwt/auth/token
  • Copy the token in the HTTP response header (jwt):

test-jwt-snippet-1

  • Issue a GET request to http://localhost:8080/jax-rs-with-jwt/resources/books and include the JWT in the HTTP Authorization header (prepend it with Bearer):
  • test-jwt-snippet-2

    I am sure there are lots of other uses of JWT itself. Hopefully this gives a decent starting point to using them within your JAX-RS services.

    JSON JWT (JSON Web Token) Requests authentication application Web Service

    Published at DZone with permission of Abhishek Gupta, DZone MVB. See the original article here.

    Opinions expressed by DZone contributors are their own.

    Related

    • What D'Hack Is DPoP?
    • Authentication With Remote LDAP Server in Spring Web MVC
    • Securing Spring Boot Microservices with JSON Web Tokens (JWT)
    • C# Applications Vulnerability Cheatsheet

    Partner Resources

    ×

    Comments
    Oops! Something Went Wrong

    The likes didn't load as expected. Please refresh the page and try again.

    ABOUT US

    • About DZone
    • Support and feedback
    • Community research
    • Sitemap

    ADVERTISE

    • Advertise with DZone

    CONTRIBUTE ON DZONE

    • Article Submission Guidelines
    • Become a Contributor
    • Core Program
    • Visit the Writers' Zone

    LEGAL

    • Terms of Service
    • Privacy Policy

    CONTACT US

    • 3343 Perimeter Hill Drive
    • Suite 100
    • Nashville, TN 37211
    • support@dzone.com

    Let's be friends:

    Likes
    There are no likes...yet! 👀
    Be the first to like this post!
    It looks like you're not logged in.
    Sign in to see who liked this post!