Microservices with Spring Boot — Authentication with JWT (Part 3)

Filter, Validate, and Generate Tokens

Omar Elgabry
OmarElgabry's Blog
5 min readJun 11, 2018

--

Authentication with JWT

🙌 The Github repository for the application: https://github.com/OmarElGabry/microservices-spring-boot

Authentication Workflow

The authentication flow is simple as:

  1. The user sends a request to get a token passing his credentials.
  2. The server validates the credentials and sends back a token.
  3. With every request, the user has to provide the token, and server will validate that token.

We’ll introduce another service called ‘auth service’ for validating user credentials, and issuing tokens.

What about validating the token? Well, it can be implemented in the auth service itself, and the gateway has to call the auth service to validate the token before allowing the requests to go to any service.

Instead, we can validate the tokens at the gateway level, and let the auth service validate user credentials, and issue tokens. And that’s what we’re going to do here.

In both ways, we are blocking the requests unless it’s authenticated (except the requests for generating tokens).

JSON Based Token (JWT)

A token is an encoded string, generated by our application (after being authenticated) and sent by the user along each request to allow access to the resources exposed by our application.

JSON Based Token (JWT) is a JSON-based open standard for creating access tokens. It consists of three parts; header, payload, and signature.

The header contains the hashing algorithm

{type: “JWT”, hash: “HS256”}

The payload contains attributes (username, email, etc) and their values.

{username: "Omar", email: "omar@example.com", admin: true }

The signature is hashing of: Header + “.” + Payload + Secret key

Gateway

In the gateway, we need to do two things: (1) validate tokens with every request, and (2) prevent all unauthenticated requests to our services. Fair enough?

In the pom.xml add spring security and JWT dependencies.

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

In application.properties add paths to auth service (we’ll create it later).

To define our security configurations, create a class, and annotated with @EnableWebSecurity, and extends WebSecurityConfigurerAdapter class to override and provide our own custom security configurations.

Spring has filters that will get executed within the life-cycle of the request (filter chain). To enable and use these filters, we need to extend the class of any of these filters.

By default spring will try to figure out when the filter should be executed. Otherwise, we can also define when should be executed (after or before another filter).

The JwtConfig is just a class contains configuration variables.

The last step is to implement our filter that validates the tokens. We’re using OncePerRequestFilter. It guarantee a single execution per request (since you can have a filter on the filter chain more than once).

Auth Service

In the auth service, we need to (1) validate the user credentials, and if valid, (2) generate a token, otherwise, throw an exception.

In the pom.xml add the following dependencies: Web, Eureka Client, Spring Security and JWT.

In the application.properties

As we did in the Gateway for security configurations, create a class, annotated with @EnableWebSecurity, and extends WebSecurityConfigurerAdapter

As you can see in the code above, we need to implement UserDetailsService interface.

This class acts like a provider for the user; meaning it loads the user from the database (or any data source). It doesn’t do authentication. It just loads the user given his username.

And here comes the last step; the filter.

We’re using JwtUsernameAndPasswordAuthenticationFilter. It’s used to validate user credentials, and generate tokens. The username and password must be sent in a POST request.

Common Service

When you have common configuration variables, enum classes, or logic, used by multiple services, like the one we had JwtConfig. Instead of duplicating the code, we put it in a separate service that can be included and used as a dependency in other services.

To do so, just create a new project (service), call it ‘common’, and follow the same steps as we did with the image service. So, In pom.xml file

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

In the application.properties

spring.application.name=common-service
server.port=9200
eureka.client.service-url.default-zone=http://localhost:8761/eureka

In the spring boot main application class

package com.eureka.common;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class SpringEurekaCommonApp {
public static void main(String[] args) {
SpringApplication.run(SpringEurekaCommonApp.class, args);
}
}

Then, copy JwtConfig class we created earlier in Gateway in common service.

package com.eureka.common.security;import org.springframework.beans.factory.annotation.Value;public class JwtConfig {
// ...
}

Now, to be able to call JwtConfig class from other services, like auth and gateway, we just need to add the common service in pom.xml as dependency.

<dependency>
<groupId>com.eureka.common</groupId>
<artifactId>spring-eureka-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

And In our auth and gateway service …

// change these lines of code
import com.eureka.zuul.security.JwtConfig;
import com.eureka.auth.security.JwtConfig;
// to reference the class in common service instead
import com.eureka.common.security.JwtConfig;

Testing our Microservices

Now we plugged in the authentication logic, we can validate credentials, issue tokens, and authenticate our users seamlessly.

So, run our Eureka Server. Then, run other services: image, gallery, common, auth, and finally, the gateway.

First, let’s try to access gallery service localhost:8762/gallery without a token. You should get Unauthorized error.

{
"timestamp": "...",
"status": 401,
"error": "Unauthorized",
"message": "No message available",
"path": "/gallery/"
}

To get a token, send user credentials to localhost:8762/auth(we hardcoded two users in UserDetailsServiceImpl class above), and make sure the Content-Type in the headers is assigned to application/json

Now, we can make a request to gallery service passing the token in the header.

If token was created for the admin user, then you should be able to access admin area of gallery service.

Again, if you are running multiple instances of gallery service, each running at a different port, then requests will be distributed equally across them.

Thank you for reading! If you enjoyed it, please clap 👏 for it.

--

--

Omar Elgabry
OmarElgabry's Blog

Software Engineer. Going to the moon 🌑. When I die, turn my blog into a story. @https://www.linkedin.com/in/omarelgabry