- Why was this project created?
- Elements included in this project
- Previous steps
- Security services
- How to use it?
- gRPC communication
- Rest API documentation
- Docker
- Previous versions of the project
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.
Below is shown a brief introduction to the subprojects included in this one:
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).
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>
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.
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.
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.
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.
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.
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.
Maven project that includes common code used in several microservices, with different useful helper classes like:
- CollectionUtil
- CollectorsUtil
- ComparatorUtil
- DateTimeUtil
- ExceptionUtil
- FunctionUtil
- JsonUtil
- MapUtil
- NumberUtil
- ObjectUtil
- PredicateUtil
- StringUtil
Generic interfaces used to provide common conversion functionality using MapStruct:
And functional programming structures and useful classes like:
There is a newer version of most above functionality using Java 21, here.
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.
In the next picture you will see a communication diagram of all microservices described above:
Due to every microservice has to decrypt the information sent by config-server, some steps are required:
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
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
andUS_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
andUS_export_policy.jar
to your$JAVA_HOME/jre/lib/security
directory.
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:
-
Run registry-server and config-server
-
Encrypt required values using the provided endpoint for that purpose, as follows:
- Overwrite current values by the provided ones.
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.
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.
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:
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:
3. Get authorization information using access token:
This microservice has an equivalent list of web services to provide the same functionality, starting with the required credentials for every request:
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:
2. Refresh authentication information after the access token expiration:
3. Get authorization information using access token:
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):
- registry-server
- config-server
- gateway-server
- security-oauth-service (if we want to use order-service)
- 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:
or:
Besides the REST API developed in:
In this project has been added a gRPC communication channel between:
- gRPC client: in order-service
- gRPC server: in pizza-service
Both use the same approach to run server and client instances:
- The instance definition:
- The Spring functionality used to run it:
The internal communication between a microservice and its related security server, that is:
Uses Basic Authentication to include the required credentials:
- order-service in the class SecurityManager
- pizza-service in the class SecurityManager
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 methodbuildCallCredentials
of the class GrpcClient. -
In the gRPC server, the interceptor AuthenticationInterceptor manages required verifications.
This project uses Spring Sleuth for distributed tracing, to simulate the same behaviour two interceptors have been defined:
- At gRPC client with RequestIdInterceptor
- At gRPC server with RequestIdInterceptor
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:
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:
The following microservices have a well documented Rest API:
Swagger has been used in all cases, however two different libraries have been included:
- SpringFox in gateway-server to unify the webpage used to access to the rest of documented microservices.
- Springdoc-OpenApi in every documented microservice.
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/
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:
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.
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).
- Go to the PostgreSQL's folder with configuration files (in my case
/etc/postgresql/12/main
) - Edit
postgresql.conf
to listen connections outsidelocalhost
:
listen_addresses = '*'
- 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
- Restart PostgreSQL service (in my case
service postgresql restart
).
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.