/Spring5Microservices

Proof of concept to create microservices using Spring 5 and related technologies

Primary LanguageJava

Spring5Microservices

Why was this project created?

Basically to know how to create a project using the microservices approach with 5th version of Spring framework. Due to there are several options we can use for different features included in a microservice architecture, the main purpose of this project is explore the most widely used creating a good base we will be able to use in a real one.

Elements included in this project

Below is shown a brief introduction to the subprojects included in this one:

registry-server

Server used to register all microservices included in this project. In this case, using Netflix Eureka each client can simultaneously act as a server, to replicate its status to a connected peer. In other words, a client retrieves a list of all connected peers of a service registry and makes all further requests to any other services through a load-balancing algorithm (Ribbon by default).

config-server

Configuration server used by the included microservices to get their required initial values like database configuration, for example. Those configuration values have been added into the project:

As you can see, there is a specific folder for every microservice and the important information is encoded (the next code is part of pizza-service-dev.yml file):

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/microservice
    username: microservice
    # Using environment variable ENCRYPT_KEY=ENCRYPT_KEY
    # Getting the value with POST localhost:8888/encrypt and the password in its body
    password: "{cipher}c5c54009a56a0f215a208067a2b13189091c13480306c81ab68edfb22a6251ca"

To increase the security level, in the config-server microservice I have deactivated the decryption in application.yml, sending the information encrypted and delegating in every microservice the labour of decrypt it. That is the reason to include in their pom.xml file, the dependency:

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-rsa</artifactId>
</dependency>



gateway-server

Using Spring Gateway, this is the gateway implementation used by the other microservices included in this proof of concept. This module contains a filter to registry every web service invoked, helping to debug each request.

security-oauth-service

Full integration with Oauth 2.0 + Jwt functionality provided by Spring, used to have an option to manage authentication/authorization functionalities through access and refresh tokens. With this microservice working as Oauth server we will be able to configure the details of every allowed application using the table in database: security.oauth_client_details. On the other hand, several customizations have been included to the manage the creation of both JWT tokens and how to append additional information too.

The technologies used are the following ones:

  • Hibernate as ORM to deal with the PostgreSQL database.
  • JPA for accessing, persisting, and managing data between Java objects and database.
  • Lombok to reduce the code development in entities and DTOs.
  • Cache2k as cache to reduce the invocations to the database.

In this microservice, the layer's division is:

  • repository layer used to access to the database.
  • service containing the business logic.

On the other hand, there are other important folders:

  • configuration with several classes used to manage several areas such: security, exception handlers, cache, etc.
  • model to store the entities.
  • dto custom objects to contain specific data.

security-jwt-service

Based on JWT token, this module was created to centralize the management of authentication/authorization functionalities. Its main purpose is provided a completely multi-application platform to generate/manage their own access and refresh tokens (including additional information), choosing between JWS or JWE token type. Every application will be able to manage its own token configuration/generation adding a new row in the database table: security.jwt_client_details and implementing the interface IAuthenticationGenerator.

The technologies used are the following ones:

  • Hibernate as ORM to deal with the PostgreSQL database.
  • JPA for accessing, persisting, and managing data between Java objects and database.
  • Lombok to reduce the code development in entities and DTOs.
  • Hazelcast as cache to reduce the invocations to the database.
  • NimbusJoseJwt to work with JWS/JWE tokens.
  • Webflux creating a reactive REST Api to manage the authentication/authorization requests.

In this microservice, the layer's division is:

  • repository layer used to access to the database.
  • service containing the business logic.
  • controller REST Api using Webflux.

On the other hand, there are other important folders:

  • configuration with several classes used to manage several areas such: security, exception handlers, cache, etc.
  • model to store the entities.
  • dto custom objects to contain specific data.
  • util to manage the JWS/JWE functionality.

pizza-service

One pizza has several ingredients, this is the summary of the entities/DTOs included on this microservice. The main purpose of this microservice is the creation of a small one on which I am using the following technologies:

  • Hibernate as ORM to deal with the PostgreSQL database.
  • JPA for accessing, persisting, and managing data between Java objects and database.
  • QueryDSL allowing us to create type-safe queries as an alternative to the "potential problematic" ones development with HQL and/or Spring JPA repository.
  • Lombok to reduce the code development in entities and DTOs.
  • Hazelcast as cache to store temporary banned users.
  • MapStruct used to conversion between Entities <--> DTOs in an easy way.
  • Webflux creating a reactive REST Api as alternative to the traditional Spring MVC.

In this microservice, the layer's division is:

  • repository layer used to access to the database.
  • service containing the business logic.
  • controller REST Api using Webflux.

On the other hand, there are other important folders:

  • configuration with several classes used to manage several areas such: persistence, exception handlers, etc.
  • model to store the entities.
  • dto custom objects to contain specific data.
  • util/converter to translate from entities to dtos and vice versa.

Using Hazelcast for that purpose, this microservice provides functionality to banned users temporally. That is the way we can use to disable any JWT active token related with a user we just disabled in database (through admin web page or similar tool). UserController class provides the required web services.

This microservice includes a gRPC server, more information in gRPC communication.

order-service

One order has several order lines and one order line contains a pizza. The main purpose of this microservice is the creation of a small one on which I am using the following technologies:

  • jOOQ replacing to the traditional pair Hibernate/JPA. Allowing us to create type-safe queries and improve the performance between the microservice and the database.
  • Lombok to reduce the code development in models and DTOs.
  • MapStruct used to conversion between Models <--> DTOs in an easy way.
  • SimpleFlatMapper due to its integration with jOOQ, used to convert some custom query results into a known Java object.
  • OpenFeign integrated with Spring Cloud to communicate this microservice and security-oauth-service.
  • MVC a traditional Spring MVC Rest API to manage the included requests.

In this microservice, the layer's division is:

  • dao layer used to access to the database.
  • service containing the business logic.
  • controller REST Api using Spring MVC.

On the other hand, there are other important folders:

  • configuration with several classes used to manage several areas such: exception handlers, etc.
  • model to store the Java objects that match with the tables in database.
  • dto custom objects to contain specific data.
  • util/converter to translate from models to dtos and vice versa.

This microservice includes a gRPC client, more information in gRPC communication.

grpc-api

Common functionality used by developed gRPC server and client. This one contains:

  • ingredient.proto with the contract which includes defining the gRPC service and the method request and response types using protocol buffers specification.

  • BasicCredential which carries the Basic Authentication that will be propagated from gRPC client to the server in the request metadata with the Authorization key.

  • GrpcErrorHandlerUtil helper class with several methods to manage errors both on gRPC client and server side.

More information about how gRPC server and client uses it in gRPC communication.

common

Maven project that includes common code used in several microservices, with different useful helper classes like:

Generic interfaces used to provide common conversion functionality using MapStruct:

And functional programming structures and useful classes like:

sql

With SQL files included in the main database and the one used for testing purpose. In both cases, there is one file with the structure of the tables and another one with the information initially included.

Communication diagram

In the next picture you will see a communication diagram of all microservices described above:

Alt text

Previous steps

Due to every microservice has to decrypt the information sent by config-server, some steps are required:

Setting up an encryption key

In this project a symmetric encryption key has been used. The symmetric encryption key is nothing more than a shared secret that's used by the encrypter to encrypt a value and the decrypter to decrypt a value. With the Spring Cloud configuration server developed in config-server, the symmetric encryption key is a string of characters you select that is passed to the service via an operating system environment variable called ENCRYPT_KEY. For those microservices, I have used:

ENCRYPT_KEY=ENCRYPT_KEY



JDK and Oracle JCE

If you are using Oracle JDK instead of OpenJDK, you need to download and install Oracle's Unlimited Strength Java Cryptography Extension (JCE). This isn't available through Maven and must be downloaded from Oracle Corporation. Once you've downloaded the zip files containing the JCE jars, you must do the following:

  • Locate your $JAVA_HOME/jre/lib/security directory

  • Back up the local_policy.jar and US_export_policy.jar files in the $JAVA_HOME/jre/lib/security directory to a different location.

  • Unzip the JCE zip file you downloaded from Oracle

  • Copy the local_policy.jar and US_export_policy.jar to your $JAVA_HOME/jre/lib/security directory.

Problems resolution

If you receive some errors related with encryption like:

IllegalStateException: Cannot decrypt: ...

Please, take a look to the previous steps in this section, maybe one of them is missing. If you still see same error messages, the best way to solve it is changing the cipher values added in the microservices configuration files included in:

Like:

spring:
  datasource:
    # Raw password: microservice
    password: "{cipher}c5c54009a56a0f215a208067a2b13189091c13480306c81ab68edfb22a6251ca"

And database table security.jwt_client_details, in the column signature_secret.

To do it:

Alt text

  • Overwrite current values by the provided ones.

Security services

As you read previously, there are two different microservices you can use to manage the authentication/authorization functionality: security-oauth-service and security-jwt-service, in this proof of concept I have used the first one in order-service and the second one to securize pizza-service.

Regarding every microservice, in this section I will explain the web services provided by every one and how to use them, starting by security-oauth-service.

security-oauth-service endpoints

Before enter in details about this security service, it is important to know that, for every request we have to include the Oauth 2.0 credentials.

Alt text

You can see the raw password in the SQL file MasterDatabase_Data.sql, when the information about this application is included in the table security.oauth_client_details. In this case, the password is Spring5Microservices.

So, the list of web services is the following one:

1. Get the authentication information:

Alt text

In the previous image, I have used for this example admin/admin, there is another option: user/user, included in the SQL file MasterDatabase_Data.sql (in the inserts related with the table eat.user).

2. Refresh authentication information after the access token expiration:

Alt text

3. Get authorization information using access token:

Alt text

security-jwt-service endpoints

This microservice has an equivalent list of web services to provide the same functionality, starting with the required credentials for every request:

Alt text

And in a similar way to the previous one, the table in database to contain that information is security.jwt_client_details.

So, the list of web services is the following one:

1. Get the authentication information:

Alt text

2. Refresh authentication information after the access token expiration:

Alt text

3. Get authorization information using access token:

Alt text

How to use it?

The first step is adding in our databases: main and test ones, the SQL files included in the sql folder. Once we have finished, it will be necessary to run the following services (following the displayed ordination):

  1. registry-server
  2. config-server
  3. gateway-server
  4. security-oauth-service (if we want to use order-service)
  5. security-jwt-service (if we want to use pizza-service)

And finally any of the other ones (or both): pizza-service and order-service.

So, as I explained you in Security services, once you have obtained the required JWT access token, you can use it to invoke the required web services:

Alt text

or:

Alt text

gRPC communication

Besides the REST API developed in:

In this project has been added a gRPC communication channel between:

Both use the same approach to run server and client instances:



Security in gRPC

The internal communication between a microservice and its related security server, that is:

Uses Basic Authentication to include the required credentials:

In the current gRPC development I have followed the same approach:

  • In the gRPC client creating a new BasicCredential instance and adding it in the Authorization header sent to the server, using the method buildCallCredentials of the class GrpcClient.

  • In the gRPC server, the interceptor AuthenticationInterceptor manages required verifications.

Request identifier in gRPC

This project uses Spring Sleuth for distributed tracing, to simulate the same behaviour two interceptors have been defined:

gRPC example request

order-service contains an endpoint to get the summary of ingredients related with an order. Such endpoint receives the order's identifier as parameter and uses it to obtain the identifiers of the related pizzas from its own database. To get the list of ingredients use the developed gRPC communication channel to receive from pizza-service the requested information.

The communication diagram including also the invocation of security-oauth-service is the following:

Alt text

So, as I explained you in security-oauth-service endpoints, once you have obtained the required JWT access token, you can use it to invoke the web service that uses the developed gRPC channel:

Alt text

Rest API documentation

The following microservices have a well documented Rest API:

Swagger has been used in all cases, however two different libraries have been included:

To facilitate access to this documentation, we can use the gateway-server URL. On that way, using the upper selector, we will be able to choose between all existing microservices. By default, the url to access them is http://localhost:5555/swagger-ui/

Alt text

Docker

In addition to launching all the microservices included in this project as normal Java applications locally, all of them have been dockerized. Every one includes a Dockerfile inside with the required configuration and instructions to generate both Docker image and container. On the other hand, new application files with docker profile have been added to Spring5Microservices_ConfigServerData.

There are 2 main types of Dockerfile based on the option to invoke maven install inside the Docker container, this is because some projects contain internal dependencies that have not been uploaded to a public repository like: common or grpc-api.

Projects with maven install in their DockerFile and which do not need to create the jar file previously:

Projects that must create the jar file before creating the Docker image:

Once you have created all the Docker images on your local, you should see something similar to:

Alt text

Docker compose

To manage the Docker containers in an easier way, a Docker compose file: compose.yml has been added. It includes the required commands to up and down the project's containers.

PostgreSQL configuration in localhost

In this project, the PostgreSQL database has not been dockerized, feel free to do it if you prefer such option instead of using the local one. In this section, I will describe the required steps to allow the connections from Docker to the PostgreSQL database installed in a local computer, in my case, PostgreSQL 12 over Ubuntu (other database versions and/or OS should need similar ones).

  1. Go to the PostgreSQL's folder with configuration files (in my case /etc/postgresql/12/main)

  2. Edit postgresql.conf to listen connections outside localhost:
listen_addresses = '*'
  1. Edit pg_hba.conf to allow connections from Docker containers
# # IPv4 local connections:
host    all             all             172.18.0.0/16           md5   # Docker
host    all             all             172.21.0.0/16           md5   # Docker compose
  1. Restart PostgreSQL service (in my case service postgresql restart).

Previous versions of the project

There are several archive git branches with previous versions of the current project:

  • Archive master equivalent to the current master but using previous versions of Spring and other libraries.

  • Archive multi datasource security specialization of security-jwt-service on which every application has its own datasource, so different persistent context are defined for every one. Uses previous versions of Spring and other libraries.

  • Archive Spring Jdbc security specialization of security-jwt-service but in this case there is only one datasource but Hibernate + JPA have been replaced by Spring JDBC template to improve the performance. Uses previous versions of Spring and other libraries.