Spring Boot Authorization: Creating an Authorization Server for your Microservices

C. R. Raja Vignesh
Javarevisited
Published in
8 min readMay 14, 2022

This article explains in detail about implementing an Authentication mechanism using a centralized Authorization Server and an API gateway.

What is a JWT Token and Why Use It?

JSON Web Token is a proposed Internet standard for creating data with optional signature and/or optional encryption whose payload holds JSON that asserts some number of claims. The tokens are signed either using a private secret or a public/private key.

They can be used to achieve stateless authentication. They can be shared across the instances or multiple services and can contain the details needed to authenticate them. Thus, there is no need to setup separate resources to maintain session or store the token/session details in a separate database/cache.

Architecture Used

The General Flow of the request to Secured resources
The General Flow of the request to Secured resources

The general design used is:

  • A centralized Authorization Server that will be used for creating and validating the JWT tokens.
  • The API Gateway will act as the point of entry for the application. This will route the requests to the corresponding microservices.
  • A Gateway filter will be added to the routes for validating the JWT tokens in the request for secured resources. This will make an API call to the Authorization Server to validate the token and get the username and authorities to the user. These details will be forwarded to the downstream services in the Header part for validations.
  • The Eureka Discovery Client will be used for Service discovery.

Authentication Flow

The general flow for the services is:

  • The user user can login/create an authentication token by calling the /login (POST) endpoint with the username and password. This will return the Bearer token as a header parameter.
  • Get the authorization token from the from the response header. This should passed as the value for the Authorization header in the format Bearer access_token for requests to secured resources.
  • For the secured resources, the custom Gateway Filter (AuthenticationPrefilter) will be called. This will do an API call to the /api/v1/validateToken Endpoint in the Authentication Service which validates the token and sends the username and authorities granted to the user in response in case of successful validation.
  • If the token is valid, the username and authorities to the user is appended to the header request before being forwarded to the resource requested by the user.
  • The other microservices (For eg: user-service) will have an authorization filter extended from OncePerRequestFilter in spring-security that will create an Authentication object using the UsernamePasswordAuthenticationToken class (with username and SimpleGrantedAuthority from header as input with password as null).
  • If the user has authority/access to a resource, then the request is allowed. Else, 401 Unathorized / 403 Forbidden response is returned to the client.

Setting Up the services:

Eureka Server

  • Create a Spring Boot application using the Spring initializr with the spring-cloud-starter-netflix-eureka-server dependency in the pom file. The spring-cloud-dependencies also has to be added under dependency management for support.
  • Now, application can be configured to start up as a Eureka Server by adding the @EnableEurekaServer annotation to the main class of the application.
  • The bellow properties are added to the properties file for supporting the Eureka Server.
spring.application.name=naming-server
server.port=8761

eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.prefer-ip-address=true
  • Now, the Eureka Server can be accessed in http://localhost:8761/ after startup. It will display a list of the services registered to it.

Authorization Service

  • Create a Spring Boot Application spring-boot-starter-security, spring-boot-starter-web, spring-cloud-starter-sleuth, spring-cloud-starter-config, spring-cloud-starter-netflix-eureka-client, spring-boot-starter-data-jpa, spring-boot-starter-data-mongodb, spring-boot-starter-data-redis and lombok dependencies.
  • The spring-boot-starter-security is needed for the Authorization and Authentication purposes while spring-boot-starter-data-mongodb and spring-boot-starter-data-jpa are needed for accessing the credentials in mongo DB collections. The dependency io.jsonwebtoken:jjwt is need for creating and validating JWT tokens.
  • For Enabling the authentication using the credentials in the database, a custom implementation of the UserDetailsService class from Spring Security has to be provided. This will implement the loadUserByUsername() method which will be used to fetch the user credentials from the database and return an instance of UserDetails (from spring security).
@Service
public class ApplicationUserDetailsService implements UserDetailsService {

@Autowired
private UsersService usersService;


@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
return new ApplicationUsers(usersService.getByUsrName(s).orElseThrow(() -> new UsernameNotFoundException("Username Not Found")));
}
}
  • A custom implementation of the UserDetails class is created to map the custom database objects to the format required by spring security.
  • A new Configuration class is created extending the WebSecurityConfigurerAdapter class in spring security.
  • Here, a Bean of the DaoAuthenticationProvider is created with a password encoder and an instance of the UserDetailsService implementation created by us.
  • This in turn is used to override the configure(AuthenticationManagerBuilder auth) method by configuring the AuthenticationManagerBuilder to use the custom Authentication Provider created by us.
  • Now, to use the JWT/Bearer tokens instead of username and password based authentication each time, we must configure two filters: One to generate the bearer token and the other to validate it.
  • JWT Generation Filter: This is created as a subclass of UsernamePasswordAuthenticationFilter in Spring Security. Here, we are overriding the attemptAuthentication() methodd to validate the credentials and successfulAuthentication() to create the JWT token in case of successful Authorization.
  • JWT Validation Filter: This filter is created extending the OncePerRequestFilter class provided by spring security and is configured by addFilterAfter() in Configuration so that it is invoked after the JWT Generation filter.

Additional Feature:

The additional process involved for generating UUID and storing the token

In general, the content of the JWT token cannot be modified once created. However, it can be easily decrypted and the content can be read by anyone.

Thus, instead of fully returning the JWT token to the user, we can instead return a random UUID generated for that Authentication request and store reference to them in a redis cache.

As such for the client facing application, the UUID generated would be returned and used while the JWT token can be shared between the services for Authorization/Authentication purposes.

API Gateway

  • Create a Springboot application with the spring-cloud-starter-gateway, spring-cloud-starter-config and spring-cloud-starter-netflix-eureka-client dependencies needed to setup an API gateway with an Eureka Client.
  • The details for the Cloud config Server and Eureka Server are added to the configuration file.
debug: true
logging:
level:
org.springframework.cloud.gateway: DEBUG
reactor.netty.http.client: DEBUG
server:
port: '8765'
spring:
cloud:
config:
profile: dev
gateway:
discovery.locator.enabled: true
config:
import: optional:configserver:http://clouduser:configserver705!@localhost:8888
application:
name: api-gateway
jackson:
date-format: yyyy-MM-dd HH:mm:ss
management:
endpoints:
web:
exposure:
include: '*'
eureka:
client:
serviceUrl:
defaultZone: http://eurekauser:eureka124!@localhost:8761/eureka
instance:
prefer-ip-address: 'true'
  • The @EnableFeignClients annotation is added to the main class of the Api Gateway Application so that it will connect to the Configured Eureka Server.
  • A Gateway Filter is configured that will validate the Bearer tokens in the requests by calling the /validateToken endpoint in the Authorization Server. This class is created by extending the AbstractGatewayFilterFactory class provided by the Spring-API Gateway and overriding its apply(Config config) method which returns an object for GatewayFilter.

Configuring the Routes

The routes are configured in a separate Configuration file and the GatewayFilter Class created above are attached to them as a filter to be applied to them.

User-Service (Configuring Spring Security and a resource endpoint)

  • Create a User-Service project using Spring initializer. This will have the user resource and is an example of a backend service.
  • A Configuration class extending the WebSecurityConfigurerAdapter class overriding it’s void configure(HttpSecurity http)
    throws Exception
    is created. Here, the Custom filter is defined to be executing the UsernamePasswordAuthenticationFilter filter in Spring Boot.
  • The JWTVerifierFilter filter will check if any username and authority data were added in the header parameter for the request (by the API Gateway) and will create an Authentication object by using UsernamePasswordAuthenticationToken class.
  • This is then set to the Security Context of the Spring Security using the SecurityContextHolder class.

Thus, the request will be allowed to the resource/endpoint if the user requesting them has access/authority to them. The bellow is a sample block for the GET method in user resource.

Common Dependencies Used:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

For Using the Spring Cloud libraries like the Config Server and Eureka Clients, a separate section must be added under dependency management in the corresponding POM files.

<properties>
<spring-cloud.version>2020.0.3</spring-cloud.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

JWT Token Usage

Validation of the requests:

  • This is the login API that is exposed at the end that can be used to Authenticate the users and generate the JWT Token.
  • The validateToken API that is used internally to validate the token being sent in the request and get the Authorization details for the token. (This API will later be blocked from external access).
  • A Secured Resource in the User-Service when called via the API gateway with a valid Authorization Token.
  • The Secured Resource in the User-Service when called via the API gateway without a valid Authorization Token.
  • A Sample JWT Token being generated by the Authorization service.

Update: You can find the Full Project at: https://github.com/Vicky-cmd/Authentication-Service.git

So that’s all in this topic for now. Reviews are always appreciated. ✌️

--

--

C. R. Raja Vignesh
Javarevisited

Currently Working as a Developer in Tata Consultancy Services on Spring Boot Applications & Microservices. Interested to learn new things about cloud and DevOps